void tcp_write(int s, short event, void *arg) { struct ctl_tcp_event *cte = arg; int err; socklen_t len; if (event == EV_TIMEOUT) { tcp_close(cte, HOST_DOWN); hce_notify_done(cte->host, HCE_TCP_CONNECT_TIMEOUT); return; } len = sizeof(err); if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len)) fatal("tcp_write: getsockopt"); if (err != 0) { tcp_close(cte, HOST_DOWN); hce_notify_done(cte->host, HCE_TCP_CONNECT_FAIL); return; } cte->host->up = HOST_UP; tcp_host_up(cte); }
void ssl_connect(int s, short event, void *arg) { struct ctl_tcp_event *cte = arg; int retry_flag = 0; int tls_err = 0; int ret; if (event == EV_TIMEOUT) { cte->host->up = HOST_DOWN; hce_notify_done(cte->host, HCE_TLS_CONNECT_TIMEOUT); ssl_cleanup(cte); return; } ret = SSL_connect(cte->ssl); if (ret <= 0) { tls_err = SSL_get_error(cte->ssl, ret); switch (tls_err) { case SSL_ERROR_WANT_READ: retry_flag = EV_READ; goto retry; case SSL_ERROR_WANT_WRITE: retry_flag = EV_WRITE; goto retry; default: cte->host->up = HOST_DOWN; ssl_error(cte->host->conf.name, "cannot connect"); hce_notify_done(cte->host, HCE_TLS_CONNECT_FAIL); ssl_cleanup(cte); return; } } if (cte->table->conf.check == CHECK_TCP) { cte->host->up = HOST_UP; hce_notify_done(cte->host, HCE_TLS_CONNECT_OK); ssl_cleanup(cte); return; } if (cte->table->sendbuf != NULL) { event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_write, &cte->tv_start, &cte->table->conf.timeout, cte); return; } if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) fatalx("ssl_connect: cannot create dynamic buffer"); event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, ssl_read, &cte->tv_start, &cte->table->conf.timeout, cte); return; retry: event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_connect, &cte->tv_start, &cte->table->conf.timeout, cte); }
void tcp_read_buf(int s, short event, void *arg) { ssize_t br; char rbuf[SMALL_READ_BUF_SIZE]; struct ctl_tcp_event *cte = arg; if (event == EV_TIMEOUT) { cte->host->up = HOST_DOWN; ibuf_free(cte->buf); close(s); hce_notify_done(cte->host, HCE_TCP_READ_TIMEOUT); return; } bzero(rbuf, sizeof(rbuf)); br = read(s, rbuf, sizeof(rbuf) - 1); switch (br) { case -1: if (errno == EAGAIN || errno == EINTR) goto retry; cte->host->up = HOST_DOWN; ibuf_free(cte->buf); close(cte->s); hce_notify_done(cte->host, HCE_TCP_READ_FAIL); return; case 0: cte->host->up = HOST_DOWN; (void)cte->validate_close(cte); close(cte->s); ibuf_free(cte->buf); hce_notify_done(cte->host, cte->host->he); return; default: if (ibuf_add(cte->buf, rbuf, br) == -1) fatal("tcp_read_buf: buf_add error"); if (cte->validate_read != NULL) { if (cte->validate_read(cte) != 0) goto retry; close(cte->s); ibuf_free(cte->buf); hce_notify_done(cte->host, cte->host->he); return; } break; /* retry */ } retry: event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, &cte->tv_start, &cte->table->conf.timeout, cte); }
void tcp_send_req(int s, short event, void *arg) { struct ctl_tcp_event *cte = arg; int bs; int len; if (event == EV_TIMEOUT) { cte->host->up = HOST_DOWN; close(cte->s); hce_notify_done(cte->host, HCE_TCP_WRITE_TIMEOUT); return; } len = strlen(cte->req); do { bs = write(s, cte->req, len); if (bs == -1) { if (errno == EAGAIN || errno == EINTR) goto retry; log_warnx("%s: cannot send request", __func__); cte->host->up = HOST_DOWN; close(cte->s); hce_notify_done(cte->host, HCE_TCP_WRITE_FAIL); return; } cte->req += bs; len -= bs; } while (len > 0); if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) fatalx("tcp_send_req: cannot create dynamic buffer"); event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, &cte->tv_start, &cte->table->conf.timeout, cte); return; retry: event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req, &cte->tv_start, &cte->table->conf.timeout, cte); }
void script_done(struct relayd *env, struct ctl_script *scr) { struct host *host; if ((host = host_find(env, scr->host)) == NULL) fatalx("hce_dispatch_parent: invalid host id"); if (scr->retval < 0) host->up = HOST_UNKNOWN; else if (scr->retval == 0) host->up = HOST_DOWN; else host->up = HOST_UP; host->flags |= F_CHECK_DONE; hce_notify_done(host, host->up == HOST_UP ? HCE_SCRIPT_OK : HCE_SCRIPT_FAIL); }
void tcp_host_up(int s, struct ctl_tcp_event *cte) { cte->s = s; switch (cte->table->conf.check) { case CHECK_TCP: if (cte->table->conf.flags & F_SSL) break; close(s); hce_notify_done(cte->host, HCE_TCP_CONNECT_OK); return; case CHECK_HTTP_CODE: cte->validate_read = NULL; cte->validate_close = check_http_code; break; case CHECK_HTTP_DIGEST: cte->validate_read = NULL; cte->validate_close = check_http_digest; break; case CHECK_SEND_EXPECT: cte->validate_read = check_send_expect; cte->validate_close = check_send_expect; break; } if (cte->table->conf.flags & F_SSL) { ssl_transaction(cte); return; } if (cte->table->sendbuf != NULL) { cte->req = cte->table->sendbuf; event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req, &cte->tv_start, &cte->table->conf.timeout, cte); return; } if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL) fatalx("tcp_host_up: cannot create dynamic buffer"); event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, tcp_read_buf, &cte->tv_start, &cte->table->conf.timeout, cte); }
void ssl_transaction(struct ctl_tcp_event *cte) { if (cte->ssl == NULL) { cte->ssl = SSL_new(cte->table->ssl_ctx); if (cte->ssl == NULL) { ssl_error(cte->host->conf.name, "cannot create object"); fatal("cannot create SSL object"); } } if (SSL_set_fd(cte->ssl, cte->s) == 0) { cte->host->up = HOST_UNKNOWN; ssl_error(cte->host->conf.name, "cannot set fd"); ssl_cleanup(cte); hce_notify_done(cte->host, HCE_TLS_CONNECT_ERROR); return; } SSL_set_connect_state(cte->ssl); event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_connect, &cte->tv_start, &cte->table->conf.timeout, cte); }
void ssl_read(int s, short event, void *arg) { char rbuf[SMALL_READ_BUF_SIZE]; struct ctl_tcp_event *cte = arg; int retry_flag = EV_READ; int tls_err = 0; int ret; if (event == EV_TIMEOUT) { cte->host->up = HOST_DOWN; ssl_cleanup(cte); hce_notify_done(cte->host, HCE_TLS_READ_TIMEOUT); return; } bzero(rbuf, sizeof(rbuf)); ret = SSL_read(cte->ssl, rbuf, sizeof(rbuf)); if (ret <= 0) { tls_err = SSL_get_error(cte->ssl, ret); switch (tls_err) { case SSL_ERROR_WANT_READ: retry_flag = EV_READ; goto retry; case SSL_ERROR_WANT_WRITE: retry_flag = EV_WRITE; goto retry; case SSL_ERROR_ZERO_RETURN: /* FALLTHROUGH */ case SSL_ERROR_SYSCALL: if (ret == 0) { cte->host->up = HOST_DOWN; (void)cte->validate_close(cte); ssl_cleanup(cte); hce_notify_done(cte->host, cte->host->he); return; } /* FALLTHROUGH */ default: cte->host->up = HOST_DOWN; ssl_error(cte->host->conf.name, "cannot read"); ssl_cleanup(cte); hce_notify_done(cte->host, HCE_TLS_READ_ERROR); break; } return; } if (ibuf_add(cte->buf, rbuf, ret) == -1) fatal("ssl_read: buf_add error"); if (cte->validate_read != NULL) { if (cte->validate_read(cte) != 0) goto retry; ssl_cleanup(cte); hce_notify_done(cte->host, cte->host->he); return; } retry: event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_read, &cte->tv_start, &cte->table->conf.timeout, cte); return; }
void check_tcp(struct ctl_tcp_event *cte) { int s; socklen_t len; struct timeval tv; struct linger lng; int he = HCE_TCP_SOCKET_OPTION; switch (cte->host->conf.ss.ss_family) { case AF_INET: ((struct sockaddr_in *)&cte->host->conf.ss)->sin_port = cte->table->conf.port; break; case AF_INET6: ((struct sockaddr_in6 *)&cte->host->conf.ss)->sin6_port = cte->table->conf.port; break; } len = ((struct sockaddr *)&cte->host->conf.ss)->sa_len; if ((s = socket(cte->host->conf.ss.ss_family, SOCK_STREAM, 0)) == -1) { if (errno == EMFILE || errno == ENFILE) he = HCE_TCP_SOCKET_LIMIT; else he = HCE_TCP_SOCKET_ERROR; goto bad; } cte->s = s; bzero(&lng, sizeof(lng)); if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) goto bad; if (cte->host->conf.ttl > 0) { if (setsockopt(s, IPPROTO_IP, IP_TTL, &cte->host->conf.ttl, sizeof(int)) == -1) goto bad; } if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) goto bad; bcopy(&cte->table->conf.timeout, &tv, sizeof(tv)); if (connect(s, (struct sockaddr *)&cte->host->conf.ss, len) == -1) { if (errno != EINPROGRESS) { he = HCE_TCP_CONNECT_FAIL; goto bad; } } cte->buf = NULL; cte->host->up = HOST_UP; event_del(&cte->ev); event_set(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_write, cte); event_add(&cte->ev, &tv); return; bad: tcp_close(cte, HOST_DOWN); hce_notify_done(cte->host, he); }