Ejemplo n.º 1
0
/*
 * This function initiates a connection to the server assigned to this session
 * (s->target, s->si[1].addr.to). It will assign a server if none
 * is assigned yet.
 * It can return one of :
 *  - SN_ERR_NONE if everything's OK
 *  - SN_ERR_SRVTO if there are no more servers
 *  - SN_ERR_SRVCL if the connection was refused by the server
 *  - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
 *  - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
 *  - SN_ERR_INTERNAL for any other purely internal errors
 * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted.
 * The server-facing stream interface is expected to hold a pre-allocated connection
 * in s->si[1].conn.
 */
int connect_server(struct session *s)
{
	struct connection *cli_conn;
	struct connection *srv_conn;
	struct server *srv;
	int reuse = 0;
	int err;

	srv_conn = objt_conn(s->si[1].end);
	if (srv_conn)
		reuse = s->target == srv_conn->target;

	if (reuse) {
		/* Disable connection reuse if a dynamic source is used.
		 * As long as we don't share connections between servers,
		 * we don't need to disable connection reuse on no-idempotent
		 * requests nor when PROXY protocol is used.
		 */
		srv = objt_server(s->target);
		if (srv && srv->conn_src.opts & CO_SRC_BIND) {
			if ((srv->conn_src.opts & CO_SRC_TPROXY_MASK) == CO_SRC_TPROXY_DYN)
				reuse = 0;
		}
		else if (s->be->conn_src.opts & CO_SRC_BIND) {
			if ((s->be->conn_src.opts & CO_SRC_TPROXY_MASK) == CO_SRC_TPROXY_DYN)
				reuse = 0;
		}
	}

	srv_conn = si_alloc_conn(&s->si[1], reuse);
	if (!srv_conn)
		return SN_ERR_RESOURCE;

	if (!(s->flags & SN_ADDR_SET)) {
		err = assign_server_address(s);
		if (err != SRV_STATUS_OK)
			return SN_ERR_INTERNAL;
	}

	if (!conn_xprt_ready(srv_conn)) {
		/* the target was only on the session, assign it to the SI now */
		srv_conn->target = s->target;

		/* set the correct protocol on the output stream interface */
		if (objt_server(s->target)) {
			conn_prepare(srv_conn, protocol_by_family(srv_conn->addr.to.ss_family), objt_server(s->target)->xprt);
		}
		else if (obj_type(s->target) == OBJ_TYPE_PROXY) {
			/* proxies exclusively run on raw_sock right now */
			conn_prepare(srv_conn, protocol_by_family(srv_conn->addr.to.ss_family), &raw_sock);
			if (!objt_conn(s->si[1].end) || !objt_conn(s->si[1].end)->ctrl)
				return SN_ERR_INTERNAL;
		}
		else
			return SN_ERR_INTERNAL;  /* how did we get there ? */

		/* process the case where the server requires the PROXY protocol to be sent */
		srv_conn->send_proxy_ofs = 0;
		if (objt_server(s->target) && objt_server(s->target)->pp_opts) {
			srv_conn->send_proxy_ofs = 1; /* must compute size */
			cli_conn = objt_conn(s->si[0].end);
			if (cli_conn)
				conn_get_to_addr(cli_conn);
		}

		si_attach_conn(&s->si[1], srv_conn);

		assign_tproxy_address(s);
	}
	else {
		/* the connection is being reused, just re-attach it */
		si_attach_conn(&s->si[1], srv_conn);
		s->flags |= SN_SRV_REUSED;
	}

	/* flag for logging source ip/port */
	if (s->fe->options2 & PR_O2_SRC_ADDR)
		s->si[1].flags |= SI_FL_SRC_ADDR;

	/* disable lingering */
	if (s->be->options & PR_O_TCP_NOLING)
		s->si[1].flags |= SI_FL_NOLINGER;

	err = si_connect(&s->si[1]);

	if (err != SN_ERR_NONE)
		return err;

	/* set connect timeout */
	s->si[1].exp = tick_add_ifset(now_ms, s->be->timeout.connect);

	srv = objt_server(s->target);
	if (srv) {
		s->flags |= SN_CURR_SESS;
		srv->cur_sess++;
		if (srv->cur_sess > srv->counters.cur_sess_max)
			srv->counters.cur_sess_max = srv->cur_sess;
		if (s->be->lbprm.server_take_conn)
			s->be->lbprm.server_take_conn(srv);
	}

	return SN_ERR_NONE;  /* connection is OK */
}
Ejemplo n.º 2
0
/*
 * Create a new peer session in assigned state (connect will start automatically)
 */
static struct stream *peer_session_create(struct peer *peer, struct peer_session *ps)
{
	struct listener *l = LIST_NEXT(&peer->peers->peers_fe->conf.listeners, struct listener *, by_fe);
	struct proxy *p = (struct proxy *)l->frontend; /* attached frontend */
	struct appctx *appctx;
	struct session *sess;
	struct stream *s;
	struct task *t;
	struct connection *conn;

	ps->reconnect = tick_add(now_ms, MS_TO_TICKS(5000));
	ps->statuscode = PEER_SESS_SC_CONNECTCODE;
	s = NULL;

	appctx = appctx_new(&peer_applet);
	if (!appctx)
		goto out_close;

	appctx->st0 = PEER_SESS_ST_CONNECT;
	appctx->ctx.peers.ptr = (void *)ps;

	sess = session_new(p, l, &appctx->obj_type);
	if (!sess) {
		Alert("out of memory in peer_session_create().\n");
		goto out_free_appctx;
	}

	if ((t = task_new()) == NULL) {
		Alert("out of memory in peer_session_create().\n");
		goto out_free_sess;
	}
	t->nice = l->nice;

	if ((s = stream_new(sess, t, &appctx->obj_type)) == NULL) {
		Alert("Failed to initialize stream in peer_session_create().\n");
		goto out_free_task;
	}

	/* The tasks below are normally what is supposed to be done by
	 * fe->accept().
	 */
	s->flags = SF_ASSIGNED|SF_ADDR_SET;

	/* applet is waiting for data */
	si_applet_cant_get(&s->si[0]);
	appctx_wakeup(appctx);

	/* initiate an outgoing connection */
	si_set_state(&s->si[1], SI_ST_ASS);

	/* automatically prepare the stream interface to connect to the
	 * pre-initialized connection in si->conn.
	 */
	if (unlikely((conn = conn_new()) == NULL))
		goto out_free_strm;

	conn_prepare(conn, peer->proto, peer->xprt);
	si_attach_conn(&s->si[1], conn);

	conn->target = s->target = &s->be->obj_type;
	memcpy(&conn->addr.to, &peer->addr, sizeof(conn->addr.to));
	s->do_log = NULL;
	s->uniq_id = 0;

	s->res.flags |= CF_READ_DONTWAIT;

	l->nbconn++; /* warning! right now, it's up to the handler to decrease this */
	p->feconn++;/* beconn will be increased later */
	jobs++;
	if (!(s->sess->listener->options & LI_O_UNLIMITED))
		actconn++;
	totalconn++;

	ps->appctx = appctx;
	ps->stream = s;
	return s;

	/* Error unrolling */
 out_free_strm:
	LIST_DEL(&s->list);
	pool_free2(pool2_stream, s);
 out_free_task:
	task_free(t);
 out_free_sess:
	session_free(sess);
 out_free_appctx:
	appctx_free(appctx);
 out_close:
	return s;
}