/* This function is called from the protocol layer accept() in order to * instanciate a new session on behalf of a given listener and frontend. It * returns a positive value upon success, 0 if the connection can be ignored, * or a negative value upon critical failure. The accepted file descriptor is * closed if we return <= 0. If no handshake is needed, it immediately tries * to instanciate a new stream. The created connection's owner points to the * new session until the upper layers are created. */ int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr) { struct connection *cli_conn; struct proxy *p = l->bind_conf->frontend; struct session *sess; int ret; ret = -1; /* assume unrecoverable error by default */ if (unlikely((cli_conn = conn_new()) == NULL)) goto out_close; conn_prepare(cli_conn, l->proto, l->bind_conf->xprt); cli_conn->handle.fd = cfd; cli_conn->addr.from = *addr; cli_conn->flags |= CO_FL_ADDR_FROM_SET; cli_conn->target = &l->obj_type; cli_conn->proxy_netns = l->netns; conn_ctrl_init(cli_conn); /* wait for a PROXY protocol header */ if (l->options & LI_O_ACC_PROXY) { cli_conn->flags |= CO_FL_ACCEPT_PROXY; conn_sock_want_recv(cli_conn); } /* wait for a NetScaler client IP insertion protocol header */ if (l->options & LI_O_ACC_CIP) { cli_conn->flags |= CO_FL_ACCEPT_CIP; conn_sock_want_recv(cli_conn); } conn_xprt_want_recv(cli_conn); if (conn_xprt_init(cli_conn) < 0) goto out_free_conn; sess = session_new(p, l, &cli_conn->obj_type); if (!sess) goto out_free_conn; conn_set_owner(cli_conn, sess, NULL); /* now evaluate the tcp-request layer4 rules. We only need a session * and no stream for these rules. */ if ((l->options & LI_O_TCP_L4_RULES) && !tcp_exec_l4_rules(sess)) { /* let's do a no-linger now to close with a single RST. */ setsockopt(cfd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); ret = 0; /* successful termination */ goto out_free_sess; } /* monitor-net and health mode are processed immediately after TCP * connection rules. This way it's possible to block them, but they * never use the lower data layers, they send directly over the socket, * as they were designed for. We first flush the socket receive buffer * in order to avoid emission of an RST by the system. We ignore any * error. */ if (unlikely((p->mode == PR_MODE_HEALTH) || ((l->options & LI_O_CHK_MONNET) && addr->ss_family == AF_INET && (((struct sockaddr_in *)addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr))) { /* we have 4 possibilities here : * - HTTP mode, from monitoring address => send "HTTP/1.0 200 OK" * - HEALTH mode with HTTP check => send "HTTP/1.0 200 OK" * - HEALTH mode without HTTP check => just send "OK" * - TCP mode from monitoring address => just close */ if (l->proto->drain) l->proto->drain(cfd); if (p->mode == PR_MODE_HTTP || (p->mode == PR_MODE_HEALTH && (p->options2 & PR_O2_CHK_ANY) == PR_O2_HTTP_CHK)) send(cfd, "HTTP/1.0 200 OK\r\n\r\n", 19, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE); else if (p->mode == PR_MODE_HEALTH) send(cfd, "OK\n", 3, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE); ret = 0; goto out_free_sess; } /* Adjust some socket options */ if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6) { setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)); if (p->options & PR_O_TCP_CLI_KA) setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one)); if (p->options & PR_O_TCP_NOLING) fdtab[cfd].linger_risk = 1; #if defined(TCP_MAXSEG) if (l->maxseg < 0) { /* we just want to reduce the current MSS by that value */ int mss; socklen_t mss_len = sizeof(mss); if (getsockopt(cfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &mss_len) == 0) { mss += l->maxseg; /* remember, it's < 0 */ setsockopt(cfd, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss)); } } #endif } if (global.tune.client_sndbuf) setsockopt(cfd, SOL_SOCKET, SO_SNDBUF, &global.tune.client_sndbuf, sizeof(global.tune.client_sndbuf)); if (global.tune.client_rcvbuf) setsockopt(cfd, SOL_SOCKET, SO_RCVBUF, &global.tune.client_rcvbuf, sizeof(global.tune.client_rcvbuf)); /* OK, now either we have a pending handshake to execute with and then * we must return to the I/O layer, or we can proceed with the end of * the stream initialization. In case of handshake, we also set the I/O * timeout to the frontend's client timeout and register a task in the * session for this purpose. The connection's owner is left to the * session during this period. * * At this point we set the relation between sess/task/conn this way : * * +----------------- task * | | * orig -- sess <-- context | * | ^ | | * v | | | * conn -- owner ---> task <-----+ */ if (cli_conn->flags & (CO_FL_HANDSHAKE | CO_FL_EARLY_SSL_HS)) { if (unlikely((sess->task = task_new(tid_bit)) == NULL)) goto out_free_sess; conn_set_xprt_done_cb(cli_conn, conn_complete_session); sess->task->context = sess; sess->task->nice = l->nice; sess->task->process = session_expire_embryonic; sess->task->expire = tick_add_ifset(now_ms, p->timeout.client); task_queue(sess->task); return 1; } /* OK let's complete stream initialization since there is no handshake */ if (conn_complete_session(cli_conn) >= 0) return 1; /* error unrolling */ out_free_sess: /* prevent call to listener_release during session_free. It will be * done below, for all errors. */ sess->listener = NULL; session_free(sess); out_free_conn: conn_stop_tracking(cli_conn); conn_xprt_close(cli_conn); conn_free(cli_conn); out_close: listener_release(l); if (ret < 0 && l->bind_conf->xprt == xprt_get(XPRT_RAW) && p->mode == PR_MODE_HTTP) { /* critical error, no more memory, try to emit a 500 response */ struct chunk *err_msg = &p->errmsg[HTTP_ERR_500]; if (!err_msg->str) err_msg = &http_err_chunks[HTTP_ERR_500]; send(cfd, err_msg->str, err_msg->len, MSG_DONTWAIT|MSG_NOSIGNAL); } if (fdtab[cfd].owner) fd_delete(cfd); else close(cfd); return ret; }
/* * 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 */ }
/* This function is called from the protocol layer accept() in order to * instanciate a new session on behalf of a given listener and frontend. It * returns a positive value upon success, 0 if the connection can be ignored, * or a negative value upon critical failure. The accepted file descriptor is * closed if we return <= 0. If no handshake is needed, it immediately tries * to instanciate a new stream. */ int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr) { struct connection *cli_conn; struct proxy *p = l->frontend; struct session *sess; struct stream *strm; struct task *t; int ret; ret = -1; /* assume unrecoverable error by default */ if (unlikely((cli_conn = conn_new()) == NULL)) goto out_close; conn_prepare(cli_conn, l->proto, l->xprt); cli_conn->t.sock.fd = cfd; cli_conn->addr.from = *addr; cli_conn->flags |= CO_FL_ADDR_FROM_SET; cli_conn->target = &l->obj_type; cli_conn->proxy_netns = l->netns; conn_ctrl_init(cli_conn); /* wait for a PROXY protocol header */ if (l->options & LI_O_ACC_PROXY) { cli_conn->flags |= CO_FL_ACCEPT_PROXY; conn_sock_want_recv(cli_conn); } conn_data_want_recv(cli_conn); if (conn_xprt_init(cli_conn) < 0) goto out_free_conn; sess = session_new(p, l, &cli_conn->obj_type); if (!sess) goto out_free_conn; p->feconn++; /* This session was accepted, count it now */ if (p->feconn > p->fe_counters.conn_max) p->fe_counters.conn_max = p->feconn; proxy_inc_fe_conn_ctr(l, p); /* now evaluate the tcp-request layer4 rules. We only need a session * and no stream for these rules. */ if ((l->options & LI_O_TCP_RULES) && !tcp_exec_req_rules(sess)) { /* let's do a no-linger now to close with a single RST. */ setsockopt(cfd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); ret = 0; /* successful termination */ goto out_free_sess; } /* monitor-net and health mode are processed immediately after TCP * connection rules. This way it's possible to block them, but they * never use the lower data layers, they send directly over the socket, * as they were designed for. We first flush the socket receive buffer * in order to avoid emission of an RST by the system. We ignore any * error. */ if (unlikely((p->mode == PR_MODE_HEALTH) || ((l->options & LI_O_CHK_MONNET) && addr->ss_family == AF_INET && (((struct sockaddr_in *)addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr))) { /* we have 4 possibilities here : * - HTTP mode, from monitoring address => send "HTTP/1.0 200 OK" * - HEALTH mode with HTTP check => send "HTTP/1.0 200 OK" * - HEALTH mode without HTTP check => just send "OK" * - TCP mode from monitoring address => just close */ if (l->proto->drain) l->proto->drain(cfd); if (p->mode == PR_MODE_HTTP || (p->mode == PR_MODE_HEALTH && (p->options2 & PR_O2_CHK_ANY) == PR_O2_HTTP_CHK)) send(cfd, "HTTP/1.0 200 OK\r\n\r\n", 19, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE); else if (p->mode == PR_MODE_HEALTH) send(cfd, "OK\n", 3, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE); ret = 0; goto out_free_sess; } /* Adjust some socket options */ if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6) { setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)); if (p->options & PR_O_TCP_CLI_KA) setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one)); if (p->options & PR_O_TCP_NOLING) fdtab[cfd].linger_risk = 1; #if defined(TCP_MAXSEG) if (l->maxseg < 0) { /* we just want to reduce the current MSS by that value */ int mss; socklen_t mss_len = sizeof(mss); if (getsockopt(cfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &mss_len) == 0) { mss += l->maxseg; /* remember, it's < 0 */ setsockopt(cfd, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss)); } } #endif } if (global.tune.client_sndbuf) setsockopt(cfd, SOL_SOCKET, SO_SNDBUF, &global.tune.client_sndbuf, sizeof(global.tune.client_sndbuf)); if (global.tune.client_rcvbuf) setsockopt(cfd, SOL_SOCKET, SO_RCVBUF, &global.tune.client_rcvbuf, sizeof(global.tune.client_rcvbuf)); if (unlikely((t = task_new()) == NULL)) goto out_free_sess; t->context = sess; t->nice = l->nice; /* OK, now either we have a pending handshake to execute with and * then we must return to the I/O layer, or we can proceed with the * end of the stream initialization. In case of handshake, we also * set the I/O timeout to the frontend's client timeout. * * At this point we set the relation between sess/task/conn this way : * * orig -- sess <-- context * | | * v | * conn -- owner ---> task */ if (cli_conn->flags & CO_FL_HANDSHAKE) { conn_attach(cli_conn, t, &sess_conn_cb); t->process = session_expire_embryonic; t->expire = tick_add_ifset(now_ms, p->timeout.client); task_queue(t); cli_conn->flags |= CO_FL_INIT_DATA | CO_FL_WAKE_DATA; return 1; } /* OK let's complete stream initialization since there is no handshake */ cli_conn->flags |= CO_FL_CONNECTED; /* we want the connection handler to notify the stream interface about updates. */ cli_conn->flags |= CO_FL_WAKE_DATA; /* if logs require transport layer information, note it on the connection */ if (sess->fe->to_log & LW_XPRT) cli_conn->flags |= CO_FL_XPRT_TRACKED; session_count_new(sess); strm = stream_new(sess, t, &cli_conn->obj_type); if (!strm) goto out_free_task; strm->target = sess->listener->default_target; strm->req.analysers = sess->listener->analysers; return 1; out_free_task: task_free(t); out_free_sess: p->feconn--; session_free(sess); out_free_conn: cli_conn->flags &= ~CO_FL_XPRT_TRACKED; conn_xprt_close(cli_conn); conn_free(cli_conn); out_close: if (ret < 0 && l->xprt == &raw_sock && p->mode == PR_MODE_HTTP) { /* critical error, no more memory, try to emit a 500 response */ struct chunk *err_msg = &p->errmsg[HTTP_ERR_500]; if (!err_msg->str) err_msg = &http_err_chunks[HTTP_ERR_500]; send(cfd, err_msg->str, err_msg->len, MSG_DONTWAIT|MSG_NOSIGNAL); } if (fdtab[cfd].owner) fd_delete(cfd); else close(cfd); return ret; }
/* * 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; }