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