1 /* $OpenBSD: check_tcp.c,v 1.39 2010/12/20 12:38:06 dhill 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 socklen_t len; 54 struct timeval tv; 55 struct linger lng; 56 int he = HCE_TCP_SOCKET_OPTION; 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 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 bzero(&lng, sizeof(lng)); 80 if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) 81 goto bad; 82 83 if (cte->host->conf.ttl > 0) { 84 if (setsockopt(s, IPPROTO_IP, IP_TTL, 85 &cte->host->conf.ttl, sizeof(int)) == -1) 86 goto bad; 87 } 88 89 if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) 90 goto bad; 91 92 bcopy(&cte->table->conf.timeout, &tv, sizeof(tv)); 93 if (connect(s, (struct sockaddr *)&cte->host->conf.ss, len) == -1) { 94 if (errno != EINPROGRESS) { 95 he = HCE_TCP_CONNECT_FAIL; 96 goto bad; 97 } 98 } 99 100 cte->buf = NULL; 101 cte->host->up = HOST_UP; 102 event_del(&cte->ev); 103 event_set(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_write, cte); 104 event_add(&cte->ev, &tv); 105 return; 106 107 bad: 108 close(s); 109 cte->host->up = HOST_DOWN; 110 hce_notify_done(cte->host, he); 111 } 112 113 void 114 tcp_write(int s, short event, void *arg) 115 { 116 struct ctl_tcp_event *cte = arg; 117 int err; 118 socklen_t len; 119 120 if (event == EV_TIMEOUT) { 121 close(s); 122 cte->host->up = HOST_DOWN; 123 hce_notify_done(cte->host, HCE_TCP_CONNECT_TIMEOUT); 124 return; 125 } 126 127 len = sizeof(err); 128 if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len)) 129 fatal("tcp_write: getsockopt"); 130 if (err != 0) { 131 close(s); 132 cte->host->up = 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(s, cte); 139 } 140 141 void 142 tcp_host_up(int s, struct ctl_tcp_event *cte) 143 { 144 cte->s = s; 145 146 switch (cte->table->conf.check) { 147 case CHECK_TCP: 148 if (cte->table->conf.flags & F_SSL) 149 break; 150 close(s); 151 hce_notify_done(cte->host, HCE_TCP_CONNECT_OK); 152 return; 153 case CHECK_HTTP_CODE: 154 cte->validate_read = NULL; 155 cte->validate_close = check_http_code; 156 break; 157 case CHECK_HTTP_DIGEST: 158 cte->validate_read = NULL; 159 cte->validate_close = check_http_digest; 160 break; 161 case CHECK_SEND_EXPECT: 162 cte->validate_read = check_send_expect; 163 cte->validate_close = check_send_expect; 164 break; 165 } 166 167 if (cte->table->conf.flags & F_SSL) { 168 ssl_transaction(cte); 169 return; 170 } 171 172 if (cte->table->sendbuf != NULL) { 173 cte->req = cte->table->sendbuf; 174 event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req, 175 &cte->tv_start, &cte->table->conf.timeout, cte); 176 return; 177 } 178 179 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) 180 fatalx("tcp_host_up: cannot create dynamic buffer"); 181 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, 182 &cte->tv_start, &cte->table->conf.timeout, cte); 183 } 184 185 void 186 tcp_send_req(int s, short event, void *arg) 187 { 188 struct ctl_tcp_event *cte = arg; 189 int bs; 190 int len; 191 192 if (event == EV_TIMEOUT) { 193 cte->host->up = HOST_DOWN; 194 close(cte->s); 195 hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT); 196 return; 197 } 198 len = strlen(cte->req); 199 do { 200 bs = write(s, cte->req, len); 201 if (bs == -1) { 202 if (errno == EAGAIN || errno == EINTR) 203 goto retry; 204 log_warnx("tcp_send_req: cannot send request"); 205 cte->host->up = HOST_DOWN; 206 close(cte->s); 207 hce_notify_done(cte->host, HCE_TCP_WRITE_FAIL); 208 return; 209 } 210 cte->req += bs; 211 len -= bs; 212 } while (len > 0); 213 214 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) 215 fatalx("tcp_send_req: cannot create dynamic buffer"); 216 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, 217 &cte->tv_start, &cte->table->conf.timeout, cte); 218 return; 219 220 retry: 221 event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req, 222 &cte->tv_start, &cte->table->conf.timeout, cte); 223 } 224 225 void 226 tcp_read_buf(int s, short event, void *arg) 227 { 228 ssize_t br; 229 char rbuf[SMALL_READ_BUF_SIZE]; 230 struct ctl_tcp_event *cte = arg; 231 232 if (event == EV_TIMEOUT) { 233 cte->host->up = HOST_DOWN; 234 ibuf_free(cte->buf); 235 close(s); 236 hce_notify_done(cte->host, HCE_TCP_READ_TIMEOUT); 237 return; 238 } 239 240 bzero(rbuf, sizeof(rbuf)); 241 br = read(s, rbuf, sizeof(rbuf) - 1); 242 switch (br) { 243 case -1: 244 if (errno == EAGAIN || errno == EINTR) 245 goto retry; 246 cte->host->up = HOST_DOWN; 247 ibuf_free(cte->buf); 248 close(cte->s); 249 hce_notify_done(cte->host, HCE_TCP_READ_FAIL); 250 return; 251 case 0: 252 cte->host->up = HOST_DOWN; 253 (void)cte->validate_close(cte); 254 close(cte->s); 255 ibuf_free(cte->buf); 256 hce_notify_done(cte->host, cte->host->he); 257 return; 258 default: 259 if (ibuf_add(cte->buf, rbuf, br) == -1) 260 fatal("tcp_read_buf: buf_add error"); 261 if (cte->validate_read != NULL) { 262 if (cte->validate_read(cte) != 0) 263 goto retry; 264 265 close(cte->s); 266 ibuf_free(cte->buf); 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 326 if (strncmp(head, "HTTP/1.1 ", strlen("HTTP/1.1 ")) && 327 strncmp(head, "HTTP/1.0 ", strlen("HTTP/1.0 "))) { 328 log_debug("check_http_code: %s failed " 329 "(cannot parse HTTP version)", host->conf.name); 330 host->up = HOST_DOWN; 331 return (1); 332 } 333 head += strlen("HTTP/1.1 "); 334 if (strlen(head) < 5) /* code + \r\n */ { 335 host->up = HOST_DOWN; 336 return (1); 337 } 338 (void)strlcpy(scode, head, sizeof(scode)); 339 code = strtonum(scode, 100, 999, &estr); 340 if (estr != NULL) { 341 log_debug("check_http_code: %s failed " 342 "(cannot parse HTTP code)", host->conf.name); 343 host->up = HOST_DOWN; 344 return (1); 345 } 346 if (code != cte->table->conf.retcode) { 347 log_debug("check_http_code: %s failed " 348 "(invalid HTTP code returned)", host->conf.name); 349 host->he = HCE_HTTP_CODE_FAIL; 350 host->up = HOST_DOWN; 351 } else { 352 host->he = HCE_HTTP_CODE_OK; 353 host->up = HOST_UP; 354 } 355 return (!(host->up == HOST_UP)); 356 } 357 358 int 359 check_http_digest(struct ctl_tcp_event *cte) 360 { 361 char *head; 362 u_char *b; 363 char digest[SHA1_DIGEST_STRING_LENGTH]; 364 struct host *host; 365 366 /* 367 * ensure string is nul-terminated. 368 */ 369 b = ibuf_reserve(cte->buf, 1); 370 if (b == NULL) 371 fatal("out of memory"); 372 *b = '\0'; 373 374 head = cte->buf->buf; 375 host = cte->host; 376 host->he = HCE_HTTP_DIGEST_ERROR; 377 378 if ((head = strstr(head, "\r\n\r\n")) == NULL) { 379 log_debug("check_http_digest: %s failed " 380 "(no end of headers)", host->conf.name); 381 host->up = HOST_DOWN; 382 return (1); 383 } 384 head += strlen("\r\n\r\n"); 385 386 digeststr(cte->table->conf.digest_type, head, strlen(head), digest); 387 388 if (strcmp(cte->table->conf.digest, digest)) { 389 log_warnx("check_http_digest: %s failed " 390 "(wrong digest)", host->conf.name); 391 host->he = HCE_HTTP_DIGEST_FAIL; 392 host->up = HOST_DOWN; 393 } else { 394 host->he = HCE_HTTP_DIGEST_OK; 395 host->up = HOST_UP; 396 } 397 return (!(host->up == HOST_UP)); 398 } 399