1 /* $OpenBSD: check_tcp.c,v 1.42 2011/06/17 14:36:51 jsg 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/param.h> 20 #include <sys/queue.h> 21 #include <sys/socket.h> 22 23 #include <net/if.h> 24 #include <netinet/in.h> 25 26 #include <limits.h> 27 #include <event.h> 28 #include <fcntl.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <stdlib.h> 32 #include <errno.h> 33 #include <fnmatch.h> 34 #include <sha1.h> 35 36 #include <openssl/ssl.h> 37 38 #include "relayd.h" 39 40 void tcp_write(int, short, void *); 41 void tcp_host_up(struct ctl_tcp_event *); 42 void tcp_close(struct ctl_tcp_event *, int); 43 void tcp_send_req(int, short, void *); 44 void tcp_read_buf(int, short, void *); 45 46 int check_http_code(struct ctl_tcp_event *); 47 int check_http_digest(struct ctl_tcp_event *); 48 int check_send_expect(struct ctl_tcp_event *); 49 50 void 51 check_tcp(struct ctl_tcp_event *cte) 52 { 53 int s; 54 socklen_t len; 55 struct timeval tv; 56 struct linger lng; 57 int he = HCE_TCP_SOCKET_OPTION; 58 59 switch (cte->host->conf.ss.ss_family) { 60 case AF_INET: 61 ((struct sockaddr_in *)&cte->host->conf.ss)->sin_port = 62 cte->table->conf.port; 63 break; 64 case AF_INET6: 65 ((struct sockaddr_in6 *)&cte->host->conf.ss)->sin6_port = 66 cte->table->conf.port; 67 break; 68 } 69 70 len = ((struct sockaddr *)&cte->host->conf.ss)->sa_len; 71 72 if ((s = socket(cte->host->conf.ss.ss_family, SOCK_STREAM, 0)) == -1) { 73 if (errno == EMFILE || errno == ENFILE) 74 he = HCE_TCP_SOCKET_LIMIT; 75 else 76 he = HCE_TCP_SOCKET_ERROR; 77 goto bad; 78 } 79 80 cte->s = s; 81 82 bzero(&lng, sizeof(lng)); 83 if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) 84 goto bad; 85 86 if (cte->host->conf.ttl > 0) { 87 if (setsockopt(s, IPPROTO_IP, IP_TTL, 88 &cte->host->conf.ttl, sizeof(int)) == -1) 89 goto bad; 90 } 91 92 if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) 93 goto bad; 94 95 bcopy(&cte->table->conf.timeout, &tv, sizeof(tv)); 96 if (connect(s, (struct sockaddr *)&cte->host->conf.ss, len) == -1) { 97 if (errno != EINPROGRESS) { 98 he = HCE_TCP_CONNECT_FAIL; 99 goto bad; 100 } 101 } 102 103 cte->buf = NULL; 104 cte->host->up = HOST_UP; 105 event_del(&cte->ev); 106 event_set(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_write, cte); 107 event_add(&cte->ev, &tv); 108 return; 109 110 bad: 111 tcp_close(cte, HOST_DOWN); 112 hce_notify_done(cte->host, he); 113 } 114 115 void 116 tcp_write(int s, short event, void *arg) 117 { 118 struct ctl_tcp_event *cte = arg; 119 int err; 120 socklen_t len; 121 122 if (event == EV_TIMEOUT) { 123 tcp_close(cte, HOST_DOWN); 124 hce_notify_done(cte->host, HCE_TCP_CONNECT_TIMEOUT); 125 return; 126 } 127 128 len = sizeof(err); 129 if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len)) 130 fatal("tcp_write: getsockopt"); 131 if (err != 0) { 132 tcp_close(cte, HOST_DOWN); 133 hce_notify_done(cte->host, HCE_TCP_CONNECT_FAIL); 134 return; 135 } 136 137 cte->host->up = HOST_UP; 138 tcp_host_up(cte); 139 } 140 141 void 142 tcp_close(struct ctl_tcp_event *cte, int status) 143 { 144 close(cte->s); 145 cte->s = -1; 146 if (status != 0) 147 cte->host->up = status; 148 if (cte->buf) { 149 ibuf_free(cte->buf); 150 cte->buf = NULL; 151 } 152 } 153 154 void 155 tcp_host_up(struct ctl_tcp_event *cte) 156 { 157 switch (cte->table->conf.check) { 158 case CHECK_TCP: 159 if (cte->table->conf.flags & F_SSL) 160 break; 161 tcp_close(cte, 0); 162 hce_notify_done(cte->host, HCE_TCP_CONNECT_OK); 163 return; 164 case CHECK_HTTP_CODE: 165 cte->validate_read = NULL; 166 cte->validate_close = check_http_code; 167 break; 168 case CHECK_HTTP_DIGEST: 169 cte->validate_read = NULL; 170 cte->validate_close = check_http_digest; 171 break; 172 case CHECK_SEND_EXPECT: 173 cte->validate_read = check_send_expect; 174 cte->validate_close = check_send_expect; 175 break; 176 } 177 178 if (cte->table->conf.flags & F_SSL) { 179 ssl_transaction(cte); 180 return; 181 } 182 183 if (cte->table->sendbuf != NULL) { 184 cte->req = cte->table->sendbuf; 185 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, tcp_send_req, 186 &cte->tv_start, &cte->table->conf.timeout, cte); 187 return; 188 } 189 190 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) 191 fatalx("tcp_host_up: cannot create dynamic buffer"); 192 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, tcp_read_buf, 193 &cte->tv_start, &cte->table->conf.timeout, cte); 194 } 195 196 void 197 tcp_send_req(int s, short event, void *arg) 198 { 199 struct ctl_tcp_event *cte = arg; 200 int bs; 201 int len; 202 203 if (event == EV_TIMEOUT) { 204 tcp_close(cte, HOST_DOWN); 205 hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT); 206 return; 207 } 208 len = strlen(cte->req); 209 do { 210 bs = write(s, cte->req, len); 211 if (bs == -1) { 212 if (errno == EAGAIN || errno == EINTR) 213 goto retry; 214 log_warnx("%s: cannot send request", __func__); 215 tcp_close(cte, HOST_DOWN); 216 hce_notify_done(cte->host, HCE_TCP_WRITE_FAIL); 217 return; 218 } 219 cte->req += bs; 220 len -= bs; 221 } while (len > 0); 222 223 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) 224 fatalx("tcp_send_req: cannot create dynamic buffer"); 225 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, 226 &cte->tv_start, &cte->table->conf.timeout, cte); 227 return; 228 229 retry: 230 event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req, 231 &cte->tv_start, &cte->table->conf.timeout, cte); 232 } 233 234 void 235 tcp_read_buf(int s, short event, void *arg) 236 { 237 ssize_t br; 238 char rbuf[SMALL_READ_BUF_SIZE]; 239 struct ctl_tcp_event *cte = arg; 240 241 if (event == EV_TIMEOUT) { 242 tcp_close(cte, HOST_DOWN); 243 hce_notify_done(cte->host, HCE_TCP_READ_TIMEOUT); 244 return; 245 } 246 247 bzero(rbuf, sizeof(rbuf)); 248 br = read(s, rbuf, sizeof(rbuf) - 1); 249 switch (br) { 250 case -1: 251 if (errno == EAGAIN || errno == EINTR) 252 goto retry; 253 tcp_close(cte, HOST_DOWN); 254 hce_notify_done(cte->host, HCE_TCP_READ_FAIL); 255 return; 256 case 0: 257 cte->host->up = HOST_DOWN; 258 (void)cte->validate_close(cte); 259 tcp_close(cte, 0); 260 hce_notify_done(cte->host, cte->host->he); 261 return; 262 default: 263 if (ibuf_add(cte->buf, rbuf, br) == -1) 264 fatal("tcp_read_buf: buf_add error"); 265 if (cte->validate_read != NULL) { 266 if (cte->validate_read(cte) != 0) 267 goto retry; 268 tcp_close(cte, 0); 269 hce_notify_done(cte->host, cte->host->he); 270 return; 271 } 272 break; /* retry */ 273 } 274 retry: 275 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, 276 &cte->tv_start, &cte->table->conf.timeout, cte); 277 } 278 279 int 280 check_send_expect(struct ctl_tcp_event *cte) 281 { 282 u_char *b; 283 284 /* 285 * ensure string is nul-terminated. 286 */ 287 b = ibuf_reserve(cte->buf, 1); 288 if (b == NULL) 289 fatal("out of memory"); 290 *b = '\0'; 291 if (fnmatch(cte->table->conf.exbuf, cte->buf->buf, 0) == 0) { 292 cte->host->he = HCE_SEND_EXPECT_OK; 293 cte->host->up = HOST_UP; 294 return (0); 295 } 296 cte->host->he = HCE_SEND_EXPECT_FAIL; 297 cte->host->up = HOST_UNKNOWN; 298 299 /* 300 * go back to original position. 301 */ 302 cte->buf->wpos--; 303 return (1); 304 } 305 306 int 307 check_http_code(struct ctl_tcp_event *cte) 308 { 309 char *head; 310 char scode[4]; 311 const char *estr; 312 u_char *b; 313 int code; 314 struct host *host; 315 316 /* 317 * ensure string is nul-terminated. 318 */ 319 b = ibuf_reserve(cte->buf, 1); 320 if (b == NULL) 321 fatal("out of memory"); 322 *b = '\0'; 323 324 head = cte->buf->buf; 325 host = cte->host; 326 host->he = HCE_HTTP_CODE_ERROR; 327 328 if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) && 329 strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) { 330 log_debug("%s: %s failed (cannot parse HTTP version)", 331 __func__, host->conf.name); 332 host->up = HOST_DOWN; 333 return (1); 334 } 335 head += strlen("HTTP/1.1 "); 336 if (strlen(head) < 5) /* code + \r\n */ { 337 host->up = HOST_DOWN; 338 return (1); 339 } 340 (void)strlcpy(scode, head, sizeof(scode)); 341 code = strtonum(scode, 100, 999, &estr); 342 if (estr != NULL) { 343 log_debug("%s: %s failed (cannot parse HTTP code)", 344 __func__, host->conf.name); 345 host->up = HOST_DOWN; 346 return (1); 347 } 348 if (code != cte->table->conf.retcode) { 349 log_debug("%s: %s failed (invalid HTTP code returned)", 350 __func__, host->conf.name); 351 host->he = HCE_HTTP_CODE_FAIL; 352 host->up = HOST_DOWN; 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