1 /* $OpenBSD: check_tcp.c,v 1.57 2019/09/15 19:23:29 rob 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_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 cte->req = cte->table->sendbuf; 188 } else if (cte->table->sendbinbuf != NULL) 189 cte->req = cte->table->sendbinbuf->buf; 190 if (cte->table->sendbuf != NULL || cte->table->sendbinbuf != NULL) { 191 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, tcp_send_req, 192 &cte->tv_start, &cte->table->conf.timeout, cte); 193 return; 194 } 195 196 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) 197 fatalx("%s: cannot create dynamic buffer", __func__); 198 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, tcp_read_buf, 199 &cte->tv_start, &cte->table->conf.timeout, cte); 200 } 201 202 void 203 tcp_send_req(int s, short event, void *arg) 204 { 205 struct ctl_tcp_event *cte = arg; 206 int bs; 207 int len; 208 209 if (event == EV_TIMEOUT) { 210 tcp_close(cte, HOST_DOWN); 211 hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT); 212 return; 213 } 214 215 if (cte->table->sendbinbuf != NULL) { 216 len = ibuf_size(cte->table->sendbinbuf); 217 log_debug("%s: table %s sending binary", __func__, 218 cte->table->conf.name); 219 print_hex(cte->table->sendbinbuf->buf, 0, len); 220 } else 221 len = strlen(cte->req); 222 223 do { 224 bs = write(s, cte->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 cte->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 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 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 log_debug("%s: table %s expecting binary", 306 __func__, cte->table->conf.name); 307 print_hex(cte->table->conf.exbinbuf, 0, 308 strlen(cte->table->conf.exbuf) / 2); 309 310 if (memcmp(cte->table->conf.exbinbuf, cte->buf->buf, 311 strlen(cte->table->conf.exbuf) / 2) == 0) { 312 cte->host->he = HCE_SEND_EXPECT_OK; 313 cte->host->up = HOST_UP; 314 return (0); 315 } else { 316 log_debug("%s: table %s received mismatching binary", 317 __func__, cte->table->conf.name); 318 print_hex(cte->buf->buf, 0, ibuf_size(cte->buf)); 319 } 320 } else if (cte->table->conf.check == CHECK_SEND_EXPECT) { 321 /* 322 * ensure string is nul-terminated. 323 */ 324 b = ibuf_reserve(cte->buf, 1); 325 if (b == NULL) 326 fatal("out of memory"); 327 *b = '\0'; 328 if (fnmatch(cte->table->conf.exbuf, cte->buf->buf, 0) == 0) { 329 cte->host->he = HCE_SEND_EXPECT_OK; 330 cte->host->up = HOST_UP; 331 return (0); 332 } 333 } 334 cte->host->he = HCE_SEND_EXPECT_FAIL; 335 cte->host->up = HOST_UNKNOWN; 336 337 /* 338 * go back to original position. 339 */ 340 cte->buf->wpos--; 341 return (1); 342 } 343 344 int 345 check_http_code(struct ctl_tcp_event *cte) 346 { 347 char *head; 348 char scode[4]; 349 const char *estr; 350 u_char *b; 351 int code; 352 struct host *host; 353 354 /* 355 * ensure string is nul-terminated. 356 */ 357 b = ibuf_reserve(cte->buf, 1); 358 if (b == NULL) 359 fatal("out of memory"); 360 *b = '\0'; 361 362 head = cte->buf->buf; 363 host = cte->host; 364 host->he = HCE_HTTP_CODE_ERROR; 365 host->code = 0; 366 367 if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) && 368 strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) { 369 log_debug("%s: %s failed (cannot parse HTTP version)", 370 __func__, host->conf.name); 371 host->up = HOST_DOWN; 372 return (1); 373 } 374 head += strlen("HTTP/1.1 "); 375 if (strlen(head) < 5) /* code + \r\n */ { 376 host->up = HOST_DOWN; 377 return (1); 378 } 379 (void)strlcpy(scode, head, sizeof(scode)); 380 code = strtonum(scode, 100, 999, &estr); 381 if (estr != NULL) { 382 log_debug("%s: %s failed (cannot parse HTTP code)", 383 __func__, host->conf.name); 384 host->up = HOST_DOWN; 385 return (1); 386 } 387 if (code != cte->table->conf.retcode) { 388 log_debug("%s: %s failed (invalid HTTP code %d returned)", 389 __func__, host->conf.name, code); 390 host->he = HCE_HTTP_CODE_FAIL; 391 host->up = HOST_DOWN; 392 host->code = code; 393 } else { 394 host->he = HCE_HTTP_CODE_OK; 395 host->up = HOST_UP; 396 } 397 return (!(host->up == HOST_UP)); 398 } 399 400 int 401 check_http_digest(struct ctl_tcp_event *cte) 402 { 403 char *head; 404 u_char *b; 405 char digest[SHA1_DIGEST_STRING_LENGTH]; 406 struct host *host; 407 408 /* 409 * ensure string is nul-terminated. 410 */ 411 b = ibuf_reserve(cte->buf, 1); 412 if (b == NULL) 413 fatal("out of memory"); 414 *b = '\0'; 415 416 head = cte->buf->buf; 417 host = cte->host; 418 host->he = HCE_HTTP_DIGEST_ERROR; 419 420 if ((head = strstr(head, "\r\n\r\n")) == NULL) { 421 log_debug("%s: %s failed (no end of headers)", 422 __func__, host->conf.name); 423 host->up = HOST_DOWN; 424 return (1); 425 } 426 head += strlen("\r\n\r\n"); 427 428 digeststr(cte->table->conf.digest_type, head, strlen(head), digest); 429 430 if (strcmp(cte->table->conf.digest, digest)) { 431 log_warnx("%s: %s failed (wrong digest)", 432 __func__, host->conf.name); 433 host->he = HCE_HTTP_DIGEST_FAIL; 434 host->up = HOST_DOWN; 435 } else { 436 host->he = HCE_HTTP_DIGEST_OK; 437 host->up = HOST_UP; 438 } 439 return (!(host->up == HOST_UP)); 440 } 441