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