blob: f48d7e8bd9ae6a57e1872696260fb7d3e0cc211c [file] [log] [blame]
Olivier Houchardfe50bfb2019-05-27 12:09:19 +02001/*
2 * Pseudo-xprt to handle any handshake except the SSL handshake
3 *
4 * Copyright 2019 HAProxy Technologies, Olivier Houchard <ohouchard@haproxy.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <proto/connection.h>
14#include <proto/stream_interface.h>
15
16struct xprt_handshake_ctx {
17 struct connection *conn;
Willy Tarreauac6febd2020-01-10 09:08:22 +010018 struct wait_event *subs;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020019 struct wait_event wait_event;
20 const struct xprt_ops *xprt;
21 void *xprt_ctx;
22};
23
24DECLARE_STATIC_POOL(xprt_handshake_ctx_pool, "xprt_handshake_ctx_pool", sizeof(struct xprt_handshake_ctx));
25
26/* This XPRT doesn't take care of sending or receiving data, once its handshake
27 * is done, it just removes itself
28 */
29static size_t xprt_handshake_from_buf(struct connection *conn, void *xprt_ctx, const struct buffer *buf, size_t count, int flags)
30{
31 return 0;
32}
33
34static size_t xprt_handshake_to_buf(struct connection *conn, void *xprt_ctx, struct buffer *buf, size_t count, int flags)
35{
36 return 0;
37}
38
39static struct task *xprt_handshake_io_cb(struct task *t, void *bctx, unsigned short state)
40{
41 struct xprt_handshake_ctx *ctx = bctx;
42 struct connection *conn = ctx->conn;
43
44 if (conn->flags & CO_FL_SOCKS4_SEND)
45 if (!conn_send_socks4_proxy_request(conn)) {
46 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_SEND,
47 &ctx->wait_event);
48
49 goto out;
50 }
51
52 if (conn->flags & CO_FL_SOCKS4_RECV)
53 if (!conn_recv_socks4_proxy_response(conn)) {
54 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
55 &ctx->wait_event);
56 goto out;
57 }
58
59 if (conn->flags & CO_FL_ACCEPT_CIP)
60 if (!conn_recv_netscaler_cip(conn, CO_FL_ACCEPT_CIP)) {
61 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
62 &ctx->wait_event);
63 goto out;
64 }
65
66 if (conn->flags & CO_FL_ACCEPT_PROXY)
67 if (!conn_recv_proxy(conn, CO_FL_ACCEPT_PROXY)) {
68 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
69 &ctx->wait_event);
70 goto out;
71 }
72
73 if (conn->flags & CO_FL_SEND_PROXY)
74 if (!conn_si_send_proxy(conn, CO_FL_SEND_PROXY)) {
75 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_SEND,
76 &ctx->wait_event);
77 goto out;
78 }
79
80out:
81 /* Wake the stream if we're done with the handshake, or we have a
82 * connection error
83 * */
84 if ((conn->flags & CO_FL_ERROR) ||
85 !(conn->flags & CO_FL_HANDSHAKE_NOSSL)) {
86 int ret = 0;
87 int woke = 0;
88 int was_conn_ctx = 0;
Willy Tarreauac6febd2020-01-10 09:08:22 +010089
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020090 /* On error, wake any waiter */
Willy Tarreauac6febd2020-01-10 09:08:22 +010091 if (ctx->subs) {
92 tasklet_wakeup(ctx->subs->tasklet);
93 ctx->subs->events = 0;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020094 woke = 1;
Willy Tarreauac6febd2020-01-10 09:08:22 +010095 ctx->subs = NULL;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020096 }
Willy Tarreauac6febd2020-01-10 09:08:22 +010097
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020098 if (!(conn->flags & CO_FL_ERROR))
99 conn->flags |= CO_FL_CONNECTED;
Willy Tarreauac6febd2020-01-10 09:08:22 +0100100
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200101 /* Remove ourself from the xprt chain */
102 if (ctx->wait_event.events != 0)
103 ctx->xprt->unsubscribe(ctx->conn,
104 ctx->xprt_ctx,
105 ctx->wait_event.events,
106 &ctx->wait_event);
107 if (conn->xprt_ctx == ctx) {
108 conn->xprt_ctx = ctx->xprt_ctx;
109 conn->xprt = ctx->xprt;
110 was_conn_ctx = 1;
111 } else
112 conn->xprt->remove_xprt(conn, conn->xprt_ctx, ctx,
113 ctx->xprt, ctx->xprt_ctx);
114 /* If we're the first xprt for the connection, let the
115 * upper layers know. If xprt_done_cb() is set, call it,
116 * and if we have a mux, and it has a wake method, call it
117 * too.
118 */
119 if (was_conn_ctx) {
120 if (ctx->conn->xprt_done_cb)
121 ret = ctx->conn->xprt_done_cb(ctx->conn);
122 if (ret >= 0 && !woke && ctx->conn->mux && ctx->conn->mux->wake)
123 ret = ctx->conn->mux->wake(ctx->conn);
124 }
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200125 tasklet_free(ctx->wait_event.tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200126 pool_free(xprt_handshake_ctx_pool, ctx);
127 }
128 return NULL;
129}
130
131static int xprt_handshake_init(struct connection *conn, void **xprt_ctx)
132{
133 struct xprt_handshake_ctx *ctx;
134 /* already initialized */
135 if (*xprt_ctx)
136 return 0;
137 if (!conn_ctrl_ready(conn))
138 return 0;
139
140 ctx = pool_alloc(xprt_handshake_ctx_pool);
141 if (!ctx) {
142 conn->err_code = CO_ER_SSL_NO_MEM;
143 return -1;
144 }
145 ctx->conn = conn;
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200146 ctx->wait_event.tasklet = tasklet_new();
147 if (!ctx->wait_event.tasklet) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200148 conn->err_code = CO_ER_SSL_NO_MEM;
149 pool_free(xprt_handshake_ctx_pool, ctx);
150 return -1;
151 }
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200152 ctx->wait_event.tasklet->process = xprt_handshake_io_cb;
153 ctx->wait_event.tasklet->context = ctx;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200154 ctx->wait_event.events = 0;
155 /* This XPRT expects the underlying XPRT to be provided later,
156 * with an add_xprt() call, so we start trying to do the handshake
157 * there, when we'll be provided an XPRT.
158 */
159 ctx->xprt = NULL;
160 ctx->xprt_ctx = NULL;
Willy Tarreauac6febd2020-01-10 09:08:22 +0100161 ctx->subs = NULL;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200162 *xprt_ctx = ctx;
163
164 return 0;
165}
166
167static void xprt_handshake_close(struct connection *conn, void *xprt_ctx)
168{
169 struct xprt_handshake_ctx *ctx = xprt_ctx;
170
171 if (ctx) {
172 if (ctx->wait_event.events != 0)
173 ctx->xprt->unsubscribe(ctx->conn, ctx->xprt_ctx,
174 ctx->wait_event.events,
175 &ctx->wait_event);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100176 if (ctx->subs) {
177 ctx->subs->events = 0;
178 tasklet_wakeup(ctx->subs->tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200179 }
180
181 if (ctx->xprt && ctx->xprt->close)
182 ctx->xprt->close(conn, ctx->xprt_ctx);
183 /* Remove any handshake flag, and if we were the connection
184 * xprt, get back to XPRT_RAW. If we're here because we
185 * failed an outoging connection, it will be retried using
186 * the same struct connection, and as xprt_handshake is a bit
187 * magic, because it requires a call to add_xprt(), it's better
188 * to fallback to the original XPRT to re-initiate the
189 * connection.
190 */
191 conn->flags &= ~CO_FL_HANDSHAKE_NOSSL;
192 if (conn->xprt == xprt_get(XPRT_HANDSHAKE))
193 conn->xprt = xprt_get(XPRT_RAW);
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200194 tasklet_free(ctx->wait_event.tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200195 pool_free(xprt_handshake_ctx_pool, ctx);
196 }
197}
198
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100199/* Called from the upper layer, to subscribe <es> to events <event_type>. The
200 * event subscriber <es> is not allowed to change from a previous call as long
201 * as at least one event is still subscribed. The <event_type> must only be a
202 * combination of SUB_RETRY_RECV and SUB_RETRY_SEND. It always returns 0.
203 */
204static int xprt_handshake_subscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es)
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200205{
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200206 struct xprt_handshake_ctx *ctx = xprt_ctx;
207
Willy Tarreauac6febd2020-01-10 09:08:22 +0100208 BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
209 BUG_ON(ctx->subs && ctx->subs->events & event_type);
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100210 BUG_ON(ctx->subs && ctx->subs != es);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100211
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100212 ctx->subs = es;
213 es->events |= event_type;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200214 return 0;
215
216}
217
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100218/* Called from the upper layer, to unsubscribe <es> from events <event_type>.
219 * The <es> pointer is not allowed to differ from the one passed to the
220 * subscribe() call. It always returns zero.
221 */
222static int xprt_handshake_unsubscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es)
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200223{
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200224 struct xprt_handshake_ctx *ctx = xprt_ctx;
225
Willy Tarreauac6febd2020-01-10 09:08:22 +0100226 BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100227 BUG_ON(ctx->subs && ctx->subs != es);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100228
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100229 es->events &= ~event_type;
230 if (!es->events)
Willy Tarreauac6febd2020-01-10 09:08:22 +0100231 ctx->subs = NULL;
232
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200233 return 0;
234}
235
236/* Use the provided XPRT as an underlying XPRT, and provide the old one.
237 * Returns 0 on success, and non-zero on failure.
238 */
239static int xprt_handshake_add_xprt(struct connection *conn, void *xprt_ctx, void *toadd_ctx, const struct xprt_ops *toadd_ops, void **oldxprt_ctx, const struct xprt_ops **oldxprt_ops)
240{
241 struct xprt_handshake_ctx *ctx = xprt_ctx;
242
243 if (oldxprt_ops)
244 *oldxprt_ops = ctx->xprt;
245 if (oldxprt_ctx)
246 *oldxprt_ctx = ctx->xprt_ctx;
247 ctx->xprt = toadd_ops;
248 ctx->xprt_ctx = toadd_ctx;
249 /* Ok we know have an xprt, so let's try to do the handshake */
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200250 tasklet_wakeup(ctx->wait_event.tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200251 return 0;
252}
253
254/* Remove the specified xprt. If if it our underlying XPRT, remove it and
255 * return 0, otherwise just call the remove_xprt method from the underlying
256 * XPRT.
257 */
258static int xprt_handshake_remove_xprt(struct connection *conn, void *xprt_ctx, void *toremove_ctx, const struct xprt_ops *newops, void *newctx)
259{
260 struct xprt_handshake_ctx *ctx = xprt_ctx;
261
262 if (ctx->xprt_ctx == toremove_ctx) {
263 ctx->xprt_ctx = newctx;
264 ctx->xprt = newops;
265 return 0;
266 }
267 return (ctx->xprt->remove_xprt(conn, ctx->xprt_ctx, toremove_ctx, newops, newctx));
268}
269
270struct xprt_ops xprt_handshake = {
271 .snd_buf = xprt_handshake_from_buf,
272 .rcv_buf = xprt_handshake_to_buf,
273 .subscribe = xprt_handshake_subscribe,
274 .unsubscribe = xprt_handshake_unsubscribe,
275 .remove_xprt = xprt_handshake_remove_xprt,
276 .add_xprt = xprt_handshake_add_xprt,
277 .init = xprt_handshake_init,
278 .close= xprt_handshake_close,
279 .rcv_pipe = NULL,
280 .snd_pipe = NULL,
281 .shutr = NULL,
282 .shutw = NULL,
283 .name = "HS",
284};
285
286__attribute__((constructor))
287static void __xprt_handshake_init(void)
288{
289 xprt_register(XPRT_HANDSHAKE, &xprt_handshake);
290}