blob: 33f775087a19955115cf157cb24995d8878f22b0 [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
Willy Tarreau7ea393d2020-06-04 18:02:10 +020013#include <haproxy/connection.h>
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020014
15struct xprt_handshake_ctx {
16 struct connection *conn;
Willy Tarreauac6febd2020-01-10 09:08:22 +010017 struct wait_event *subs;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020018 struct wait_event wait_event;
19 const struct xprt_ops *xprt;
20 void *xprt_ctx;
21};
22
Willy Tarreaub8dec4a2022-06-23 11:02:08 +020023DECLARE_STATIC_POOL(xprt_handshake_ctx_pool, "xprt_handshake_ctx", sizeof(struct xprt_handshake_ctx));
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020024
25/* This XPRT doesn't take care of sending or receiving data, once its handshake
26 * is done, it just removes itself
27 */
28static size_t xprt_handshake_from_buf(struct connection *conn, void *xprt_ctx, const struct buffer *buf, size_t count, int flags)
29{
30 return 0;
31}
32
33static size_t xprt_handshake_to_buf(struct connection *conn, void *xprt_ctx, struct buffer *buf, size_t count, int flags)
34{
35 return 0;
36}
37
Willy Tarreau691d5032021-01-20 14:55:01 +010038/* xprt_handshake_io_cb is exported to see it resolved in "show fd" */
Willy Tarreau144f84a2021-03-02 16:09:26 +010039struct task *xprt_handshake_io_cb(struct task *t, void *bctx, unsigned int state)
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020040{
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)
Christopher Fauletaa91d622022-04-01 13:22:50 +020074 if (!conn_send_proxy(conn, CO_FL_SEND_PROXY)) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020075 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) ||
Willy Tarreau4450b582020-01-23 15:23:13 +010085 !(conn->flags & CO_FL_HANDSHAKE)) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020086 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 /* Remove ourself from the xprt chain */
99 if (ctx->wait_event.events != 0)
100 ctx->xprt->unsubscribe(ctx->conn,
101 ctx->xprt_ctx,
102 ctx->wait_event.events,
103 &ctx->wait_event);
104 if (conn->xprt_ctx == ctx) {
105 conn->xprt_ctx = ctx->xprt_ctx;
106 conn->xprt = ctx->xprt;
107 was_conn_ctx = 1;
108 } else
109 conn->xprt->remove_xprt(conn, conn->xprt_ctx, ctx,
110 ctx->xprt, ctx->xprt_ctx);
111 /* If we're the first xprt for the connection, let the
Olivier Houchard477902b2020-01-22 18:08:48 +0100112 * upper layers know. If no mux was set up yet, then call
113 * conn_create_mux, and if we have a mux, and it has a wake
114 * method, call it too.
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200115 */
116 if (was_conn_ctx) {
Olivier Houchard477902b2020-01-22 18:08:48 +0100117 if (!ctx->conn->mux)
118 ret = conn_create_mux(ctx->conn);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200119 if (ret >= 0 && !woke && ctx->conn->mux && ctx->conn->mux->wake)
120 ret = ctx->conn->mux->wake(ctx->conn);
121 }
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200122 tasklet_free(ctx->wait_event.tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200123 pool_free(xprt_handshake_ctx_pool, ctx);
Willy Tarreau74163142021-03-13 11:30:19 +0100124 t = NULL;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200125 }
Willy Tarreau74163142021-03-13 11:30:19 +0100126 return t;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200127}
128
Olivier Houchardd54ede72021-03-05 23:42:41 +0100129static int xprt_handshake_start(struct connection *conn, void *xprt_ctx)
130{
131 struct xprt_handshake_ctx *ctx = xprt_ctx;
132
133 if (ctx->xprt->start) {
134 int ret;
135
136 ret = ctx->xprt->start(conn, ctx->xprt_ctx);
137 if (ret < 0)
138 return ret;
139 }
140 tasklet_wakeup(ctx->wait_event.tasklet);
141
142 return 0;
143}
144
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200145static int xprt_handshake_init(struct connection *conn, void **xprt_ctx)
146{
147 struct xprt_handshake_ctx *ctx;
148 /* already initialized */
149 if (*xprt_ctx)
150 return 0;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200151
152 ctx = pool_alloc(xprt_handshake_ctx_pool);
153 if (!ctx) {
154 conn->err_code = CO_ER_SSL_NO_MEM;
155 return -1;
156 }
157 ctx->conn = conn;
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200158 ctx->wait_event.tasklet = tasklet_new();
159 if (!ctx->wait_event.tasklet) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200160 conn->err_code = CO_ER_SSL_NO_MEM;
161 pool_free(xprt_handshake_ctx_pool, ctx);
162 return -1;
163 }
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200164 ctx->wait_event.tasklet->process = xprt_handshake_io_cb;
165 ctx->wait_event.tasklet->context = ctx;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200166 ctx->wait_event.events = 0;
Olivier Houchardd54ede72021-03-05 23:42:41 +0100167
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200168 ctx->xprt = NULL;
169 ctx->xprt_ctx = NULL;
Willy Tarreauac6febd2020-01-10 09:08:22 +0100170 ctx->subs = NULL;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200171 *xprt_ctx = ctx;
172
173 return 0;
174}
175
176static void xprt_handshake_close(struct connection *conn, void *xprt_ctx)
177{
178 struct xprt_handshake_ctx *ctx = xprt_ctx;
179
180 if (ctx) {
181 if (ctx->wait_event.events != 0)
182 ctx->xprt->unsubscribe(ctx->conn, ctx->xprt_ctx,
183 ctx->wait_event.events,
184 &ctx->wait_event);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100185 if (ctx->subs) {
186 ctx->subs->events = 0;
187 tasklet_wakeup(ctx->subs->tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200188 }
189
190 if (ctx->xprt && ctx->xprt->close)
191 ctx->xprt->close(conn, ctx->xprt_ctx);
192 /* Remove any handshake flag, and if we were the connection
193 * xprt, get back to XPRT_RAW. If we're here because we
194 * failed an outoging connection, it will be retried using
195 * the same struct connection, and as xprt_handshake is a bit
196 * magic, because it requires a call to add_xprt(), it's better
197 * to fallback to the original XPRT to re-initiate the
198 * connection.
199 */
Willy Tarreau4450b582020-01-23 15:23:13 +0100200 conn->flags &= ~CO_FL_HANDSHAKE;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200201 if (conn->xprt == xprt_get(XPRT_HANDSHAKE))
202 conn->xprt = xprt_get(XPRT_RAW);
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200203 tasklet_free(ctx->wait_event.tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200204 pool_free(xprt_handshake_ctx_pool, ctx);
205 }
206}
207
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100208/* Called from the upper layer, to subscribe <es> to events <event_type>. The
209 * event subscriber <es> is not allowed to change from a previous call as long
210 * as at least one event is still subscribed. The <event_type> must only be a
211 * combination of SUB_RETRY_RECV and SUB_RETRY_SEND. It always returns 0.
212 */
213static int xprt_handshake_subscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es)
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200214{
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200215 struct xprt_handshake_ctx *ctx = xprt_ctx;
216
Willy Tarreauac6febd2020-01-10 09:08:22 +0100217 BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100218 BUG_ON(ctx->subs && ctx->subs != es);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100219
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100220 ctx->subs = es;
221 es->events |= event_type;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200222 return 0;
223
224}
225
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100226/* Called from the upper layer, to unsubscribe <es> from events <event_type>.
227 * The <es> pointer is not allowed to differ from the one passed to the
228 * subscribe() call. It always returns zero.
229 */
230static int xprt_handshake_unsubscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es)
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200231{
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200232 struct xprt_handshake_ctx *ctx = xprt_ctx;
233
Willy Tarreauac6febd2020-01-10 09:08:22 +0100234 BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100235 BUG_ON(ctx->subs && ctx->subs != es);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100236
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100237 es->events &= ~event_type;
238 if (!es->events)
Willy Tarreauac6febd2020-01-10 09:08:22 +0100239 ctx->subs = NULL;
240
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200241 return 0;
242}
243
244/* Use the provided XPRT as an underlying XPRT, and provide the old one.
245 * Returns 0 on success, and non-zero on failure.
246 */
247static 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)
248{
249 struct xprt_handshake_ctx *ctx = xprt_ctx;
250
251 if (oldxprt_ops)
252 *oldxprt_ops = ctx->xprt;
253 if (oldxprt_ctx)
254 *oldxprt_ctx = ctx->xprt_ctx;
255 ctx->xprt = toadd_ops;
256 ctx->xprt_ctx = toadd_ctx;
Olivier Houchardd54ede72021-03-05 23:42:41 +0100257
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200258 return 0;
259}
260
261/* Remove the specified xprt. If if it our underlying XPRT, remove it and
262 * return 0, otherwise just call the remove_xprt method from the underlying
263 * XPRT.
264 */
265static int xprt_handshake_remove_xprt(struct connection *conn, void *xprt_ctx, void *toremove_ctx, const struct xprt_ops *newops, void *newctx)
266{
267 struct xprt_handshake_ctx *ctx = xprt_ctx;
268
269 if (ctx->xprt_ctx == toremove_ctx) {
270 ctx->xprt_ctx = newctx;
271 ctx->xprt = newops;
272 return 0;
273 }
274 return (ctx->xprt->remove_xprt(conn, ctx->xprt_ctx, toremove_ctx, newops, newctx));
275}
276
277struct xprt_ops xprt_handshake = {
278 .snd_buf = xprt_handshake_from_buf,
279 .rcv_buf = xprt_handshake_to_buf,
280 .subscribe = xprt_handshake_subscribe,
281 .unsubscribe = xprt_handshake_unsubscribe,
282 .remove_xprt = xprt_handshake_remove_xprt,
283 .add_xprt = xprt_handshake_add_xprt,
284 .init = xprt_handshake_init,
Olivier Houchardd54ede72021-03-05 23:42:41 +0100285 .start = xprt_handshake_start,
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200286 .close= xprt_handshake_close,
287 .rcv_pipe = NULL,
288 .snd_pipe = NULL,
289 .shutr = NULL,
290 .shutw = NULL,
291 .name = "HS",
292};
293
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200294static void __xprt_handshake_init(void)
295{
296 xprt_register(XPRT_HANDSHAKE, &xprt_handshake);
297}
Willy Tarreau79367f92022-04-25 19:18:24 +0200298
299INITCALL0(STG_REGISTER, __xprt_handshake_init);