GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/relayd/check_tcp.c Lines: 0 208 0.0 %
Date: 2017-11-13 Branches: 0 96 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: check_tcp.c,v 1.55 2017/07/04 20:27:09 benno Exp $	*/
2
3
/*
4
 * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/time.h>
21
#include <sys/socket.h>
22
23
#include <netinet/in.h>
24
25
#include <limits.h>
26
#include <event.h>
27
#include <fcntl.h>
28
#include <unistd.h>
29
#include <string.h>
30
#include <stdlib.h>
31
#include <errno.h>
32
#include <fnmatch.h>
33
#include <sha1.h>
34
#include <imsg.h>
35
36
#include "relayd.h"
37
38
void	tcp_write(int, short, void *);
39
void	tcp_host_up(struct ctl_tcp_event *);
40
void	tcp_close(struct ctl_tcp_event *, int);
41
void	tcp_send_req(int, short, void *);
42
void	tcp_read_buf(int, short, void *);
43
44
int	check_http_code(struct ctl_tcp_event *);
45
int	check_http_digest(struct ctl_tcp_event *);
46
int	check_send_expect(struct ctl_tcp_event *);
47
48
void
49
check_tcp(struct ctl_tcp_event *cte)
50
{
51
	int			 s;
52
	socklen_t		 len;
53
	struct timeval		 tv;
54
	struct linger		 lng;
55
	int			 he = HCE_TCP_SOCKET_OPTION;
56
57
	switch (cte->host->conf.ss.ss_family) {
58
	case AF_INET:
59
		((struct sockaddr_in *)&cte->host->conf.ss)->sin_port =
60
			cte->table->conf.port;
61
		break;
62
	case AF_INET6:
63
		((struct sockaddr_in6 *)&cte->host->conf.ss)->sin6_port =
64
			cte->table->conf.port;
65
		break;
66
	}
67
68
	len = ((struct sockaddr *)&cte->host->conf.ss)->sa_len;
69
70
	if ((s = socket(cte->host->conf.ss.ss_family,
71
	    SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
72
		if (errno == EMFILE || errno == ENFILE)
73
			he = HCE_TCP_SOCKET_LIMIT;
74
		else
75
			he = HCE_TCP_SOCKET_ERROR;
76
		goto bad;
77
	}
78
79
	cte->s = s;
80
81
	bzero(&lng, sizeof(lng));
82
	if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1)
83
		goto bad;
84
85
	if (cte->host->conf.ttl > 0)
86
		switch (cte->host->conf.ss.ss_family) {
87
		case AF_INET:
88
			if (setsockopt(s, IPPROTO_IP, IP_TTL,
89
			    &cte->host->conf.ttl, sizeof(int)) == -1)
90
				goto bad;
91
			break;
92
		case AF_INET6:
93
			if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
94
			    &cte->host->conf.ttl, sizeof(int)) == -1)
95
				goto bad;
96
			break;
97
		}
98
99
	bcopy(&cte->table->conf.timeout, &tv, sizeof(tv));
100
	if (connect(s, (struct sockaddr *)&cte->host->conf.ss, len) == -1) {
101
		if (errno != EINPROGRESS) {
102
			he = HCE_TCP_CONNECT_FAIL;
103
			goto bad;
104
		}
105
	}
106
107
	cte->buf = NULL;
108
	cte->host->up = HOST_UP;
109
	event_del(&cte->ev);
110
	event_set(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_write, cte);
111
	event_add(&cte->ev, &tv);
112
	return;
113
114
bad:
115
	tcp_close(cte, HOST_DOWN);
116
	hce_notify_done(cte->host, he);
117
}
118
119
void
120
tcp_write(int s, short event, void *arg)
121
{
122
	struct ctl_tcp_event	*cte = arg;
123
	int			 err;
124
	socklen_t		 len;
125
126
	if (event == EV_TIMEOUT) {
127
		tcp_close(cte, HOST_DOWN);
128
		hce_notify_done(cte->host, HCE_TCP_CONNECT_TIMEOUT);
129
		return;
130
	}
131
132
	len = sizeof(err);
133
	if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len))
134
		fatal("%s: getsockopt", __func__);
135
	if (err != 0) {
136
		tcp_close(cte, HOST_DOWN);
137
		hce_notify_done(cte->host, HCE_TCP_CONNECT_FAIL);
138
		return;
139
	}
140
141
	cte->host->up = HOST_UP;
142
	tcp_host_up(cte);
143
}
144
145
void
146
tcp_close(struct ctl_tcp_event *cte, int status)
147
{
148
	close(cte->s);
149
	cte->s = -1;
150
	if (status != 0)
151
		cte->host->up = status;
152
	ibuf_free(cte->buf);
153
	cte->buf = NULL;
154
}
155
156
void
157
tcp_host_up(struct ctl_tcp_event *cte)
158
{
159
	switch (cte->table->conf.check) {
160
	case CHECK_TCP:
161
		if (cte->table->conf.flags & F_TLS)
162
			break;
163
		tcp_close(cte, 0);
164
		hce_notify_done(cte->host, HCE_TCP_CONNECT_OK);
165
		return;
166
	case CHECK_HTTP_CODE:
167
		cte->validate_read = NULL;
168
		cte->validate_close = check_http_code;
169
		break;
170
	case CHECK_HTTP_DIGEST:
171
		cte->validate_read = NULL;
172
		cte->validate_close = check_http_digest;
173
		break;
174
	case CHECK_SEND_EXPECT:
175
		cte->validate_read = check_send_expect;
176
		cte->validate_close = check_send_expect;
177
		break;
178
	}
179
180
	if (cte->table->conf.flags & F_TLS) {
181
		check_tls(cte);
182
		return;
183
	}
184
185
	if (cte->table->sendbuf != NULL) {
186
		cte->req = cte->table->sendbuf;
187
		event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, tcp_send_req,
188
		    &cte->tv_start, &cte->table->conf.timeout, cte);
189
		return;
190
	}
191
192
	if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
193
		fatalx("%s: cannot create dynamic buffer", __func__);
194
	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, tcp_read_buf,
195
	    &cte->tv_start, &cte->table->conf.timeout, cte);
196
}
197
198
void
199
tcp_send_req(int s, short event, void *arg)
200
{
201
	struct ctl_tcp_event	*cte = arg;
202
	int			 bs;
203
	int			 len;
204
205
	if (event == EV_TIMEOUT) {
206
		tcp_close(cte, HOST_DOWN);
207
		hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT);
208
		return;
209
	}
210
	len = strlen(cte->req);
211
	do {
212
		bs = write(s, cte->req, len);
213
		if (bs == -1) {
214
			if (errno == EAGAIN || errno == EINTR)
215
				goto retry;
216
			log_warn("%s: cannot send request", __func__);
217
			tcp_close(cte, HOST_DOWN);
218
			hce_notify_done(cte->host, HCE_TCP_WRITE_FAIL);
219
			return;
220
		}
221
		cte->req += bs;
222
		len -= bs;
223
	} while (len > 0);
224
225
	if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
226
		fatalx("%s: cannot create dynamic buffer", __func__);
227
	event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf,
228
	    &cte->tv_start, &cte->table->conf.timeout, cte);
229
	return;
230
231
 retry:
232
	event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req,
233
	    &cte->tv_start, &cte->table->conf.timeout, cte);
234
}
235
236
void
237
tcp_read_buf(int s, short event, void *arg)
238
{
239
	ssize_t			 br;
240
	char			 rbuf[SMALL_READ_BUF_SIZE];
241
	struct ctl_tcp_event	*cte = arg;
242
243
	if (event == EV_TIMEOUT) {
244
		if (ibuf_size(cte->buf))
245
			(void)cte->validate_close(cte);
246
		else
247
			cte->host->he = HCE_TCP_READ_TIMEOUT;
248
		tcp_close(cte, cte->host->up == HOST_UP ? 0 : HOST_DOWN);
249
		hce_notify_done(cte->host, cte->host->he);
250
		return;
251
	}
252
253
	bzero(rbuf, sizeof(rbuf));
254
	br = read(s, rbuf, sizeof(rbuf) - 1);
255
	switch (br) {
256
	case -1:
257
		if (errno == EAGAIN || errno == EINTR)
258
			goto retry;
259
		tcp_close(cte, HOST_DOWN);
260
		hce_notify_done(cte->host, HCE_TCP_READ_FAIL);
261
		return;
262
	case 0:
263
		cte->host->up = HOST_DOWN;
264
		(void)cte->validate_close(cte);
265
		tcp_close(cte, 0);
266
		hce_notify_done(cte->host, cte->host->he);
267
		return;
268
	default:
269
		if (ibuf_add(cte->buf, rbuf, br) == -1)
270
			fatal("%s: buf_add error", __func__);
271
		if (cte->validate_read != NULL) {
272
			if (cte->validate_read(cte) != 0)
273
				goto retry;
274
			tcp_close(cte, 0);
275
			hce_notify_done(cte->host, cte->host->he);
276
			return;
277
		}
278
		break; /* retry */
279
	}
280
retry:
281
	event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf,
282
	    &cte->tv_start, &cte->table->conf.timeout, cte);
283
}
284
285
int
286
check_send_expect(struct ctl_tcp_event *cte)
287
{
288
	u_char	*b;
289
290
	/*
291
	 * ensure string is nul-terminated.
292
	 */
293
	b = ibuf_reserve(cte->buf, 1);
294
	if (b == NULL)
295
		fatal("out of memory");
296
	*b = '\0';
297
	if (fnmatch(cte->table->conf.exbuf, cte->buf->buf, 0) == 0) {
298
		cte->host->he = HCE_SEND_EXPECT_OK;
299
		cte->host->up = HOST_UP;
300
		return (0);
301
	}
302
	cte->host->he = HCE_SEND_EXPECT_FAIL;
303
	cte->host->up = HOST_UNKNOWN;
304
305
	/*
306
	 * go back to original position.
307
	 */
308
	cte->buf->wpos--;
309
	return (1);
310
}
311
312
int
313
check_http_code(struct ctl_tcp_event *cte)
314
{
315
	char		*head;
316
	char		 scode[4];
317
	const char	*estr;
318
	u_char		*b;
319
	int		 code;
320
	struct host	*host;
321
322
	/*
323
	 * ensure string is nul-terminated.
324
	 */
325
	b = ibuf_reserve(cte->buf, 1);
326
	if (b == NULL)
327
		fatal("out of memory");
328
	*b = '\0';
329
330
	head = cte->buf->buf;
331
	host = cte->host;
332
	host->he = HCE_HTTP_CODE_ERROR;
333
	host->code = 0;
334
335
	if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) &&
336
	    strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) {
337
		log_debug("%s: %s failed (cannot parse HTTP version)",
338
		    __func__, host->conf.name);
339
		host->up = HOST_DOWN;
340
		return (1);
341
	}
342
	head += strlen("HTTP/1.1 ");
343
	if (strlen(head) < 5) /* code + \r\n */ {
344
		host->up = HOST_DOWN;
345
		return (1);
346
	}
347
	(void)strlcpy(scode, head, sizeof(scode));
348
	code = strtonum(scode, 100, 999, &estr);
349
	if (estr != NULL) {
350
		log_debug("%s: %s failed (cannot parse HTTP code)",
351
		    __func__, host->conf.name);
352
		host->up = HOST_DOWN;
353
		return (1);
354
	}
355
	if (code != cte->table->conf.retcode) {
356
		log_debug("%s: %s failed (invalid HTTP code %d returned)",
357
		    __func__, host->conf.name, code);
358
		host->he = HCE_HTTP_CODE_FAIL;
359
		host->up = HOST_DOWN;
360
		host->code = code;
361
	} else {
362
		host->he = HCE_HTTP_CODE_OK;
363
		host->up = HOST_UP;
364
	}
365
	return (!(host->up == HOST_UP));
366
}
367
368
int
369
check_http_digest(struct ctl_tcp_event *cte)
370
{
371
	char		*head;
372
	u_char		*b;
373
	char		 digest[SHA1_DIGEST_STRING_LENGTH];
374
	struct host	*host;
375
376
	/*
377
	 * ensure string is nul-terminated.
378
	 */
379
	b = ibuf_reserve(cte->buf, 1);
380
	if (b == NULL)
381
		fatal("out of memory");
382
	*b = '\0';
383
384
	head = cte->buf->buf;
385
	host = cte->host;
386
	host->he = HCE_HTTP_DIGEST_ERROR;
387
388
	if ((head = strstr(head, "\r\n\r\n")) == NULL) {
389
		log_debug("%s: %s failed (no end of headers)",
390
		    __func__, host->conf.name);
391
		host->up = HOST_DOWN;
392
		return (1);
393
	}
394
	head += strlen("\r\n\r\n");
395
396
	digeststr(cte->table->conf.digest_type, head, strlen(head), digest);
397
398
	if (strcmp(cte->table->conf.digest, digest)) {
399
		log_warnx("%s: %s failed (wrong digest)",
400
		    __func__, host->conf.name);
401
		host->he = HCE_HTTP_DIGEST_FAIL;
402
		host->up = HOST_DOWN;
403
	} else {
404
		host->he = HCE_HTTP_DIGEST_OK;
405
		host->up = HOST_UP;
406
	}
407
	return (!(host->up == HOST_UP));
408
}