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