1 /* $OpenBSD: check_tcp.c,v 1.61 2023/07/03 09:38:08 claudio 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
check_tcp(struct ctl_tcp_event * cte)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
tcp_write(int s,short event,void * arg)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
tcp_close(struct ctl_tcp_event * cte,int status)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
tcp_host_up(struct ctl_tcp_event * cte)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_BINSEND_EXPECT:
175 case CHECK_SEND_EXPECT:
176 cte->validate_read = check_send_expect;
177 cte->validate_close = check_send_expect;
178 break;
179 }
180
181 if (cte->table->conf.flags & F_TLS) {
182 check_tls(cte);
183 return;
184 }
185
186 if (cte->table->sendbuf != NULL || cte->table->sendbinbuf != NULL) {
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
tcp_send_req(int s,short event,void * arg)199 tcp_send_req(int s, short event, void *arg)
200 {
201 struct ctl_tcp_event *cte = arg;
202 char *req;
203 int bs;
204 int len;
205
206 if (event == EV_TIMEOUT) {
207 tcp_close(cte, HOST_DOWN);
208 hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT);
209 return;
210 }
211
212 if (cte->table->sendbinbuf != NULL) {
213 len = ibuf_size(cte->table->sendbinbuf);
214 req = ibuf_data(cte->table->sendbinbuf);
215 log_debug("%s: table %s sending binary", __func__,
216 cte->table->conf.name);
217 print_hex(req, 0, len);
218 } else {
219 len = strlen(cte->table->sendbuf);
220 req = cte->table->sendbuf;
221 }
222
223 do {
224 bs = write(s, req, len);
225 if (bs == -1) {
226 if (errno == EAGAIN || errno == EINTR)
227 goto retry;
228 log_warn("%s: cannot send request", __func__);
229 tcp_close(cte, HOST_DOWN);
230 hce_notify_done(cte->host, HCE_TCP_WRITE_FAIL);
231 return;
232 }
233 req += bs;
234 len -= bs;
235 } while (len > 0);
236
237 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
238 fatalx("%s: cannot create dynamic buffer", __func__);
239 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf,
240 &cte->tv_start, &cte->table->conf.timeout, cte);
241 return;
242
243 retry:
244 event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req,
245 &cte->tv_start, &cte->table->conf.timeout, cte);
246 }
247
248 void
tcp_read_buf(int s,short event,void * arg)249 tcp_read_buf(int s, short event, void *arg)
250 {
251 ssize_t br;
252 char rbuf[SMALL_READ_BUF_SIZE];
253 struct ctl_tcp_event *cte = arg;
254
255 if (event == EV_TIMEOUT) {
256 if (ibuf_size(cte->buf))
257 (void)cte->validate_close(cte);
258 else {
259 cte->host->he = HCE_TCP_READ_TIMEOUT;
260 cte->host->up = HOST_DOWN;
261 }
262 tcp_close(cte, cte->host->up == HOST_UP ? 0 : HOST_DOWN);
263 hce_notify_done(cte->host, cte->host->he);
264 return;
265 }
266
267 bzero(rbuf, sizeof(rbuf));
268 br = read(s, rbuf, sizeof(rbuf) - 1);
269 switch (br) {
270 case -1:
271 if (errno == EAGAIN || errno == EINTR)
272 goto retry;
273 tcp_close(cte, HOST_DOWN);
274 hce_notify_done(cte->host, HCE_TCP_READ_FAIL);
275 return;
276 case 0:
277 cte->host->up = HOST_DOWN;
278 (void)cte->validate_close(cte);
279 tcp_close(cte, 0);
280 hce_notify_done(cte->host, cte->host->he);
281 return;
282 default:
283 if (ibuf_add(cte->buf, rbuf, br) == -1)
284 fatal("%s: buf_add error", __func__);
285 if (cte->validate_read != NULL) {
286 if (cte->validate_read(cte) != 0)
287 goto retry;
288 tcp_close(cte, 0);
289 hce_notify_done(cte->host, cte->host->he);
290 return;
291 }
292 break; /* retry */
293 }
294 retry:
295 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf,
296 &cte->tv_start, &cte->table->conf.timeout, cte);
297 }
298
299 int
check_send_expect(struct ctl_tcp_event * cte)300 check_send_expect(struct ctl_tcp_event *cte)
301 {
302 u_char *b;
303
304 if (cte->table->conf.check == CHECK_BINSEND_EXPECT) {
305 size_t exlen;
306
307 exlen = strlen(cte->table->conf.exbuf) / 2;
308 log_debug("%s: table %s expecting binary",
309 __func__, cte->table->conf.name);
310 print_hex(cte->table->conf.exbinbuf, 0, exlen);
311
312 if (ibuf_size(cte->buf) >= exlen && memcmp(ibuf_data(cte->buf),
313 cte->table->conf.exbinbuf, exlen) == 0) {
314 cte->host->he = HCE_SEND_EXPECT_OK;
315 cte->host->up = HOST_UP;
316 return (0);
317 } else if (ibuf_size(cte->buf) >= exlen) {
318 log_debug("%s: table %s received mismatching binary",
319 __func__, cte->table->conf.name);
320 print_hex(ibuf_data(cte->buf), 0, ibuf_size(cte->buf));
321 }
322 } else if (cte->table->conf.check == CHECK_SEND_EXPECT) {
323 /*
324 * ensure string is nul-terminated.
325 */
326 b = strndup(ibuf_data(cte->buf), ibuf_size(cte->buf));
327 if (b == NULL)
328 fatal("out of memory");
329 if (fnmatch(cte->table->conf.exbuf, b, 0) == 0) {
330 cte->host->he = HCE_SEND_EXPECT_OK;
331 cte->host->up = HOST_UP;
332 free(b);
333 return (0);
334 }
335 free(b);
336 }
337
338 cte->host->he = HCE_SEND_EXPECT_FAIL;
339 cte->host->up = HOST_UNKNOWN;
340 return (1);
341 }
342
343 int
check_http_code(struct ctl_tcp_event * cte)344 check_http_code(struct ctl_tcp_event *cte)
345 {
346 char *head;
347 char scode[4];
348 const char *estr;
349 int code;
350 struct host *host;
351
352 /*
353 * ensure string is nul-terminated.
354 */
355 if (ibuf_add_zero(cte->buf, 1) == -1)
356 fatal("out of memory");
357
358 head = ibuf_data(cte->buf);
359 host = cte->host;
360 host->he = HCE_HTTP_CODE_ERROR;
361 host->code = 0;
362
363 if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) &&
364 strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) {
365 log_debug("%s: %s failed (cannot parse HTTP version)",
366 __func__, host->conf.name);
367 host->up = HOST_DOWN;
368 return (1);
369 }
370 head += strlen("HTTP/1.1 ");
371 if (strlen(head) < 5) /* code + \r\n */ {
372 host->up = HOST_DOWN;
373 return (1);
374 }
375 (void)strlcpy(scode, head, sizeof(scode));
376 code = strtonum(scode, 100, 999, &estr);
377 if (estr != NULL) {
378 log_debug("%s: %s failed (cannot parse HTTP code)",
379 __func__, host->conf.name);
380 host->up = HOST_DOWN;
381 return (1);
382 }
383 if (code != cte->table->conf.retcode) {
384 log_debug("%s: %s failed (invalid HTTP code %d returned)",
385 __func__, host->conf.name, code);
386 host->he = HCE_HTTP_CODE_FAIL;
387 host->up = HOST_DOWN;
388 host->code = code;
389 } else {
390 host->he = HCE_HTTP_CODE_OK;
391 host->up = HOST_UP;
392 }
393 return (!(host->up == HOST_UP));
394 }
395
396 int
check_http_digest(struct ctl_tcp_event * cte)397 check_http_digest(struct ctl_tcp_event *cte)
398 {
399 char *head;
400 char digest[SHA1_DIGEST_STRING_LENGTH];
401 struct host *host;
402
403 /*
404 * ensure string is nul-terminated.
405 */
406 if (ibuf_add_zero(cte->buf, 1) == -1)
407 fatal("out of memory");
408
409 head = ibuf_data(cte->buf);
410 host = cte->host;
411 host->he = HCE_HTTP_DIGEST_ERROR;
412
413 if ((head = strstr(head, "\r\n\r\n")) == NULL) {
414 log_debug("%s: %s failed (no end of headers)",
415 __func__, host->conf.name);
416 host->up = HOST_DOWN;
417 return (1);
418 }
419 head += strlen("\r\n\r\n");
420
421 digeststr(cte->table->conf.digest_type, head, strlen(head), digest);
422
423 if (strcmp(cte->table->conf.digest, digest)) {
424 log_warnx("%s: %s failed (wrong digest)",
425 __func__, host->conf.name);
426 host->he = HCE_HTTP_DIGEST_FAIL;
427 host->up = HOST_DOWN;
428 } else {
429 host->he = HCE_HTTP_DIGEST_OK;
430 host->up = HOST_UP;
431 }
432 return (!(host->up == HOST_UP));
433 }
434