Example #1
0
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);
}
Example #2
0
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);
}
Example #3
0
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);
}
Example #4
0
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);
}
Example #6
0
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);
}
Example #7
0
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);
}
Example #8
0
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;
}
Example #9
0
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);
}