1 /* $OpenBSD: check_tcp.c,v 1.52 2017/02/09 11:16:22 reyk 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 if (setsockopt(s, IPPROTO_IP, IP_TTL, 87 &cte->host->conf.ttl, sizeof(int)) == -1) 88 goto bad; 89 } 90 91 bcopy(&cte->table->conf.timeout, &tv, sizeof(tv)); 92 if (connect(s, (struct sockaddr *)&cte->host->conf.ss, len) == -1) { 93 if (errno != EINPROGRESS) { 94 he = HCE_TCP_CONNECT_FAIL; 95 goto bad; 96 } 97 } 98 99 cte->buf = NULL; 100 cte->host->up = HOST_UP; 101 event_del(&cte->ev); 102 event_set(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_write, cte); 103 event_add(&cte->ev, &tv); 104 return; 105 106 bad: 107 tcp_close(cte, HOST_DOWN); 108 hce_notify_done(cte->host, he); 109 } 110 111 void 112 tcp_write(int s, short event, void *arg) 113 { 114 struct ctl_tcp_event *cte = arg; 115 int err; 116 socklen_t len; 117 118 if (event == EV_TIMEOUT) { 119 tcp_close(cte, HOST_DOWN); 120 hce_notify_done(cte->host, HCE_TCP_CONNECT_TIMEOUT); 121 return; 122 } 123 124 len = sizeof(err); 125 if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len)) 126 fatal("tcp_write: getsockopt"); 127 if (err != 0) { 128 tcp_close(cte, HOST_DOWN); 129 hce_notify_done(cte->host, HCE_TCP_CONNECT_FAIL); 130 return; 131 } 132 133 cte->host->up = HOST_UP; 134 tcp_host_up(cte); 135 } 136 137 void 138 tcp_close(struct ctl_tcp_event *cte, int status) 139 { 140 close(cte->s); 141 cte->s = -1; 142 if (status != 0) 143 cte->host->up = status; 144 ibuf_free(cte->buf); 145 cte->buf = NULL; 146 } 147 148 void 149 tcp_host_up(struct ctl_tcp_event *cte) 150 { 151 switch (cte->table->conf.check) { 152 case CHECK_TCP: 153 if (cte->table->conf.flags & F_TLS) 154 break; 155 tcp_close(cte, 0); 156 hce_notify_done(cte->host, HCE_TCP_CONNECT_OK); 157 return; 158 case CHECK_HTTP_CODE: 159 cte->validate_read = NULL; 160 cte->validate_close = check_http_code; 161 break; 162 case CHECK_HTTP_DIGEST: 163 cte->validate_read = NULL; 164 cte->validate_close = check_http_digest; 165 break; 166 case CHECK_SEND_EXPECT: 167 cte->validate_read = check_send_expect; 168 cte->validate_close = check_send_expect; 169 break; 170 } 171 172 if (cte->table->conf.flags & F_TLS) { 173 ssl_transaction(cte); 174 return; 175 } 176 177 if (cte->table->sendbuf != NULL) { 178 cte->req = cte->table->sendbuf; 179 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, tcp_send_req, 180 &cte->tv_start, &cte->table->conf.timeout, cte); 181 return; 182 } 183 184 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) 185 fatalx("tcp_host_up: cannot create dynamic buffer"); 186 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, tcp_read_buf, 187 &cte->tv_start, &cte->table->conf.timeout, cte); 188 } 189 190 void 191 tcp_send_req(int s, short event, void *arg) 192 { 193 struct ctl_tcp_event *cte = arg; 194 int bs; 195 int len; 196 197 if (event == EV_TIMEOUT) { 198 tcp_close(cte, HOST_DOWN); 199 hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT); 200 return; 201 } 202 len = strlen(cte->req); 203 do { 204 bs = write(s, cte->req, len); 205 if (bs == -1) { 206 if (errno == EAGAIN || errno == EINTR) 207 goto retry; 208 log_warn("%s: cannot send request", __func__); 209 tcp_close(cte, HOST_DOWN); 210 hce_notify_done(cte->host, HCE_TCP_WRITE_FAIL); 211 return; 212 } 213 cte->req += bs; 214 len -= bs; 215 } while (len > 0); 216 217 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) 218 fatalx("tcp_send_req: cannot create dynamic buffer"); 219 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, 220 &cte->tv_start, &cte->table->conf.timeout, cte); 221 return; 222 223 retry: 224 event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req, 225 &cte->tv_start, &cte->table->conf.timeout, cte); 226 } 227 228 void 229 tcp_read_buf(int s, short event, void *arg) 230 { 231 ssize_t br; 232 char rbuf[SMALL_READ_BUF_SIZE]; 233 struct ctl_tcp_event *cte = arg; 234 235 if (event == EV_TIMEOUT) { 236 if (ibuf_size(cte->buf)) 237 (void)cte->validate_close(cte); 238 else 239 cte->host->he = HCE_TCP_READ_TIMEOUT; 240 tcp_close(cte, cte->host->up == HOST_UP ? 0 : HOST_DOWN); 241 hce_notify_done(cte->host, cte->host->he); 242 return; 243 } 244 245 bzero(rbuf, sizeof(rbuf)); 246 br = read(s, rbuf, sizeof(rbuf) - 1); 247 switch (br) { 248 case -1: 249 if (errno == EAGAIN || errno == EINTR) 250 goto retry; 251 tcp_close(cte, HOST_DOWN); 252 hce_notify_done(cte->host, HCE_TCP_READ_FAIL); 253 return; 254 case 0: 255 cte->host->up = HOST_DOWN; 256 (void)cte->validate_close(cte); 257 tcp_close(cte, 0); 258 hce_notify_done(cte->host, cte->host->he); 259 return; 260 default: 261 if (ibuf_add(cte->buf, rbuf, br) == -1) 262 fatal("tcp_read_buf: buf_add error"); 263 if (cte->validate_read != NULL) { 264 if (cte->validate_read(cte) != 0) 265 goto retry; 266 tcp_close(cte, 0); 267 hce_notify_done(cte->host, cte->host->he); 268 return; 269 } 270 break; /* retry */ 271 } 272 retry: 273 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, 274 &cte->tv_start, &cte->table->conf.timeout, cte); 275 } 276 277 int 278 check_send_expect(struct ctl_tcp_event *cte) 279 { 280 u_char *b; 281 282 /* 283 * ensure string is nul-terminated. 284 */ 285 b = ibuf_reserve(cte->buf, 1); 286 if (b == NULL) 287 fatal("out of memory"); 288 *b = '\0'; 289 if (fnmatch(cte->table->conf.exbuf, cte->buf->buf, 0) == 0) { 290 cte->host->he = HCE_SEND_EXPECT_OK; 291 cte->host->up = HOST_UP; 292 return (0); 293 } 294 cte->host->he = HCE_SEND_EXPECT_FAIL; 295 cte->host->up = HOST_UNKNOWN; 296 297 /* 298 * go back to original position. 299 */ 300 cte->buf->wpos--; 301 return (1); 302 } 303 304 int 305 check_http_code(struct ctl_tcp_event *cte) 306 { 307 char *head; 308 char scode[4]; 309 const char *estr; 310 u_char *b; 311 int code; 312 struct host *host; 313 314 /* 315 * ensure string is nul-terminated. 316 */ 317 b = ibuf_reserve(cte->buf, 1); 318 if (b == NULL) 319 fatal("out of memory"); 320 *b = '\0'; 321 322 head = cte->buf->buf; 323 host = cte->host; 324 host->he = HCE_HTTP_CODE_ERROR; 325 host->code = 0; 326 327 if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) && 328 strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) { 329 log_debug("%s: %s failed (cannot parse HTTP version)", 330 __func__, host->conf.name); 331 host->up = HOST_DOWN; 332 return (1); 333 } 334 head += strlen("HTTP/1.1 "); 335 if (strlen(head) < 5) /* code + \r\n */ { 336 host->up = HOST_DOWN; 337 return (1); 338 } 339 (void)strlcpy(scode, head, sizeof(scode)); 340 code = strtonum(scode, 100, 999, &estr); 341 if (estr != NULL) { 342 log_debug("%s: %s failed (cannot parse HTTP code)", 343 __func__, host->conf.name); 344 host->up = HOST_DOWN; 345 return (1); 346 } 347 if (code != cte->table->conf.retcode) { 348 log_debug("%s: %s failed (invalid HTTP code %d returned)", 349 __func__, host->conf.name, code); 350 host->he = HCE_HTTP_CODE_FAIL; 351 host->up = HOST_DOWN; 352 host->code = code; 353 } else { 354 host->he = HCE_HTTP_CODE_OK; 355 host->up = HOST_UP; 356 } 357 return (!(host->up == HOST_UP)); 358 } 359 360 int 361 check_http_digest(struct ctl_tcp_event *cte) 362 { 363 char *head; 364 u_char *b; 365 char digest[SHA1_DIGEST_STRING_LENGTH]; 366 struct host *host; 367 368 /* 369 * ensure string is nul-terminated. 370 */ 371 b = ibuf_reserve(cte->buf, 1); 372 if (b == NULL) 373 fatal("out of memory"); 374 *b = '\0'; 375 376 head = cte->buf->buf; 377 host = cte->host; 378 host->he = HCE_HTTP_DIGEST_ERROR; 379 380 if ((head = strstr(head, "\r\n\r\n")) == NULL) { 381 log_debug("%s: %s failed (no end of headers)", 382 __func__, host->conf.name); 383 host->up = HOST_DOWN; 384 return (1); 385 } 386 head += strlen("\r\n\r\n"); 387 388 digeststr(cte->table->conf.digest_type, head, strlen(head), digest); 389 390 if (strcmp(cte->table->conf.digest, digest)) { 391 log_warnx("%s: %s failed (wrong digest)", 392 __func__, host->conf.name); 393 host->he = HCE_HTTP_DIGEST_FAIL; 394 host->up = HOST_DOWN; 395 } else { 396 host->he = HCE_HTTP_DIGEST_OK; 397 host->up = HOST_UP; 398 } 399 return (!(host->up == HOST_UP)); 400 } 401