#include #include #include #include #include #include #include #include #include #include #include #include #include #include struct proto_fam proto_fam_reverse_connect = { .name = "reverse_connect", .sock_domain = AF_CUST_REV_SRV, .sock_family = AF_INET, .bind = rev_bind_receiver, }; struct protocol proto_reverse_connect = { .name = "rev", /* connection layer */ .listen = rev_bind_listener, .enable = rev_enable_listener, .disable = rev_disable_listener, .add = default_add_listener, .unbind = rev_unbind_receiver, .resume = default_resume_listener, .accept_conn = rev_accept_conn, .set_affinity = rev_set_affinity, /* address family */ .fam = &proto_fam_reverse_connect, /* socket layer */ .proto_type = PROTO_TYPE_STREAM, .sock_type = SOCK_STREAM, .sock_prot = IPPROTO_TCP, .rx_listening = rev_accepting_conn, .receivers = LIST_HEAD_INIT(proto_reverse_connect.receivers), }; static struct connection *new_reverse_conn(struct listener *l, struct server *srv) { struct connection *conn = conn_new(srv); if (!conn) goto err; conn_set_reverse(conn, &l->obj_type); /* These options is incompatible with a reverse connection. */ BUG_ON(srv->conn_src.opts & CO_SRC_BIND); BUG_ON(srv->proxy->conn_src.opts & CO_SRC_BIND); sockaddr_alloc(&conn->dst, 0, 0); if (!conn->dst) goto err; *conn->dst = srv->addr; if (conn_prepare(conn, protocol_lookup(conn->dst->ss_family, PROTO_TYPE_STREAM, 0), srv->xprt)) goto err; /* TODO simplification of tcp_connect_server() */ conn->handle.fd = sock_create_server_socket(conn); if (fd_set_nonblock(conn->handle.fd) == -1) goto err; if (conn->ctrl->connect(conn, 0) != SF_ERR_NONE) goto err; if (conn_xprt_start(conn) < 0) goto err; if (!srv->use_ssl || (!srv->ssl_ctx.alpn_str && !srv->ssl_ctx.npn_str) || srv->mux_proto) { if (conn_install_mux_be(conn, NULL, NULL, NULL) < 0) goto err; } /* Not expected here. */ BUG_ON((conn->flags & CO_FL_HANDSHAKE)); return conn; err: if (conn) { /* Mark connection as non-reversable. This prevents conn_free() * to reschedule reverse_connect task on freeing a preconnect * connection. */ conn->reverse.target = NULL; conn_free(conn); } return NULL; } struct task *rev_process(struct task *task, void *ctx, unsigned int state) { struct listener *l = ctx; struct connection *conn = l->rx.reverse_connect.pend_conn; if (conn) { if (conn->flags & CO_FL_ERROR) { conn_full_close(conn); conn_free(conn); l->rx.reverse_connect.pend_conn = NULL; /* Retry on 1s on error. */ l->rx.reverse_connect.task->expire = MS_TO_TICKS(now_ms + 1000); } else { /* Spurrious receiver task wake up when pend_conn is not ready/on error. */ BUG_ON(!(conn->flags & CO_FL_REVERSED)); /* A connection is ready to be accepted. */ listener_accept(l); l->rx.reverse_connect.task->expire = TICK_ETERNITY; } } else { /* No pending reverse connection, prepare a new one. Store it in the * listener and return NULL. Connection will be returned later after * reversal is completed. */ conn = new_reverse_conn(l, l->rx.reverse_connect.srv); l->rx.reverse_connect.pend_conn = conn; /* On success task will be woken up by H2 mux after reversal. */ l->rx.reverse_connect.task->expire = conn ? TICK_ETERNITY : MS_TO_TICKS(now_ms + 1000); } return task; } int rev_bind_receiver(struct receiver *rx, char **errmsg) { rx->flags |= RX_F_BOUND; return ERR_NONE; } int rev_bind_listener(struct listener *listener, char *errmsg, int errlen) { struct task *task; struct proxy *be; struct server *srv; struct ist be_name, sv_name; char *name = NULL; /* TODO for the moment reverse conn creation is pinned to the first thread only. */ if (!(task = task_new_here())) { snprintf(errmsg, errlen, "Out of memory."); goto err; } task->process = rev_process; task->context = listener; listener->rx.reverse_connect.task = task; name = strdup(listener->bind_conf->reverse_srvname); if (!name) { snprintf(errmsg, errlen, "Out of memory."); goto err; } sv_name = ist(name); be_name = istsplit(&sv_name, '/'); if (!istlen(sv_name)) { snprintf(errmsg, errlen, "Invalid server name: '%s'.", name); goto err; } if (!(be = proxy_be_by_name(ist0(be_name)))) { snprintf(errmsg, errlen, "No such backend: '%s'.", name); goto err; } if (!(srv = server_find_by_name(be, ist0(sv_name)))) { snprintf(errmsg, errlen, "No such server: '%s/%s'.", ist0(be_name), ist0(sv_name)); goto err; } /* TODO check que on utilise pas un serveur @reverse */ if (srv->flags & SRV_F_REVERSE) { snprintf(errmsg, errlen, "Cannot use reverse server '%s/%s' as target to a reverse bind.", ist0(be_name), ist0(sv_name)); goto err; } /* Check that server uses HTTP/2 either with proto or ALPN. */ if ((!srv->mux_proto || !isteqi(srv->mux_proto->token, ist("h2"))) && (!srv->use_ssl || !isteqi(ist(srv->ssl_ctx.alpn_str), ist("\x02h2")))) { snprintf(errmsg, errlen, "Cannot reverse connect with server '%s/%s' unless HTTP/2 is activated on it with either proto or alpn keyword.", name, ist0(sv_name)); goto err; } ha_free(&name); listener->rx.reverse_connect.srv = srv; listener_set_state(listener, LI_LISTEN); task_wakeup(listener->rx.reverse_connect.task, TASK_WOKEN_ANY); return ERR_NONE; err: ha_free(&name); return ERR_ALERT | ERR_FATAL; } void rev_enable_listener(struct listener *l) { task_wakeup(l->rx.reverse_connect.task, TASK_WOKEN_ANY); } void rev_disable_listener(struct listener *l) { } struct connection *rev_accept_conn(struct listener *l, int *status) { struct connection *conn = l->rx.reverse_connect.pend_conn; if (!conn) { /* Instantiate a new conn if maxconn not yet exceeded. */ if (l->bind_conf->maxconn && l->nbconn <= l->bind_conf->maxconn) { l->rx.reverse_connect.pend_conn = new_reverse_conn(l, l->rx.reverse_connect.srv); if (!l->rx.reverse_connect.pend_conn) { *status = CO_AC_PAUSE; return NULL; } } *status = CO_AC_DONE; return NULL; } /* listener_accept() must not be called if no pending connection is not yet reversed. */ BUG_ON(!(conn->flags & CO_FL_REVERSED)); conn->flags &= ~CO_FL_REVERSED; conn->mux->ctl(conn, MUX_REVERSE_CONN, NULL); l->rx.reverse_connect.pend_conn = NULL; *status = CO_AC_NONE; return conn; } void rev_unbind_receiver(struct listener *l) { l->rx.flags &= ~RX_F_BOUND; } int rev_set_affinity(struct connection *conn, int new_tid) { /* TODO reversal conn rebinding after is disabled for the moment as we * did not test possible race conditions. */ return -1; } int rev_accepting_conn(const struct receiver *rx) { return 1; } INITCALL1(STG_REGISTER, protocol_register, &proto_reverse_connect);