blob: e9b22b8ae1139727d100c34d6e03c79360588c98 [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>
Willy Tarreau5e539c92020-06-04 20:45:39 +020014#include <haproxy/stream_interface.h>
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020015
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
Willy Tarreau691d5032021-01-20 14:55:01 +010039/* xprt_handshake_io_cb is exported to see it resolved in "show fd" */
Willy Tarreau144f84a2021-03-02 16:09:26 +010040struct task *xprt_handshake_io_cb(struct task *t, void *bctx, unsigned int state)
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020041{
42 struct xprt_handshake_ctx *ctx = bctx;
43 struct connection *conn = ctx->conn;
44
45 if (conn->flags & CO_FL_SOCKS4_SEND)
46 if (!conn_send_socks4_proxy_request(conn)) {
47 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_SEND,
48 &ctx->wait_event);
49
50 goto out;
51 }
52
53 if (conn->flags & CO_FL_SOCKS4_RECV)
54 if (!conn_recv_socks4_proxy_response(conn)) {
55 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
56 &ctx->wait_event);
57 goto out;
58 }
59
60 if (conn->flags & CO_FL_ACCEPT_CIP)
61 if (!conn_recv_netscaler_cip(conn, CO_FL_ACCEPT_CIP)) {
62 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
63 &ctx->wait_event);
64 goto out;
65 }
66
67 if (conn->flags & CO_FL_ACCEPT_PROXY)
68 if (!conn_recv_proxy(conn, CO_FL_ACCEPT_PROXY)) {
69 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
70 &ctx->wait_event);
71 goto out;
72 }
73
74 if (conn->flags & CO_FL_SEND_PROXY)
75 if (!conn_si_send_proxy(conn, CO_FL_SEND_PROXY)) {
76 ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_SEND,
77 &ctx->wait_event);
78 goto out;
79 }
80
81out:
82 /* Wake the stream if we're done with the handshake, or we have a
83 * connection error
84 * */
85 if ((conn->flags & CO_FL_ERROR) ||
Willy Tarreau4450b582020-01-23 15:23:13 +010086 !(conn->flags & CO_FL_HANDSHAKE)) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020087 int ret = 0;
88 int woke = 0;
89 int was_conn_ctx = 0;
Willy Tarreauac6febd2020-01-10 09:08:22 +010090
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020091 /* On error, wake any waiter */
Willy Tarreauac6febd2020-01-10 09:08:22 +010092 if (ctx->subs) {
93 tasklet_wakeup(ctx->subs->tasklet);
94 ctx->subs->events = 0;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020095 woke = 1;
Willy Tarreauac6febd2020-01-10 09:08:22 +010096 ctx->subs = NULL;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020097 }
Willy Tarreauac6febd2020-01-10 09:08:22 +010098
Olivier Houchardfe50bfb2019-05-27 12:09:19 +020099 /* Remove ourself from the xprt chain */
100 if (ctx->wait_event.events != 0)
101 ctx->xprt->unsubscribe(ctx->conn,
102 ctx->xprt_ctx,
103 ctx->wait_event.events,
104 &ctx->wait_event);
105 if (conn->xprt_ctx == ctx) {
106 conn->xprt_ctx = ctx->xprt_ctx;
107 conn->xprt = ctx->xprt;
108 was_conn_ctx = 1;
109 } else
110 conn->xprt->remove_xprt(conn, conn->xprt_ctx, ctx,
111 ctx->xprt, ctx->xprt_ctx);
112 /* If we're the first xprt for the connection, let the
Olivier Houchard477902b2020-01-22 18:08:48 +0100113 * upper layers know. If no mux was set up yet, then call
114 * conn_create_mux, and if we have a mux, and it has a wake
115 * method, call it too.
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200116 */
117 if (was_conn_ctx) {
Olivier Houchard477902b2020-01-22 18:08:48 +0100118 if (!ctx->conn->mux)
119 ret = conn_create_mux(ctx->conn);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200120 if (ret >= 0 && !woke && ctx->conn->mux && ctx->conn->mux->wake)
121 ret = ctx->conn->mux->wake(ctx->conn);
122 }
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200123 tasklet_free(ctx->wait_event.tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200124 pool_free(xprt_handshake_ctx_pool, ctx);
Willy Tarreau74163142021-03-13 11:30:19 +0100125 t = NULL;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200126 }
Willy Tarreau74163142021-03-13 11:30:19 +0100127 return t;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200128}
129
Olivier Houchardd54ede72021-03-05 23:42:41 +0100130static int xprt_handshake_start(struct connection *conn, void *xprt_ctx)
131{
132 struct xprt_handshake_ctx *ctx = xprt_ctx;
133
134 if (ctx->xprt->start) {
135 int ret;
136
137 ret = ctx->xprt->start(conn, ctx->xprt_ctx);
138 if (ret < 0)
139 return ret;
140 }
141 tasklet_wakeup(ctx->wait_event.tasklet);
142
143 return 0;
144}
145
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200146static int xprt_handshake_init(struct connection *conn, void **xprt_ctx)
147{
148 struct xprt_handshake_ctx *ctx;
149 /* already initialized */
150 if (*xprt_ctx)
151 return 0;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200152
153 ctx = pool_alloc(xprt_handshake_ctx_pool);
154 if (!ctx) {
155 conn->err_code = CO_ER_SSL_NO_MEM;
156 return -1;
157 }
158 ctx->conn = conn;
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200159 ctx->wait_event.tasklet = tasklet_new();
160 if (!ctx->wait_event.tasklet) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200161 conn->err_code = CO_ER_SSL_NO_MEM;
162 pool_free(xprt_handshake_ctx_pool, ctx);
163 return -1;
164 }
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200165 ctx->wait_event.tasklet->process = xprt_handshake_io_cb;
166 ctx->wait_event.tasklet->context = ctx;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200167 ctx->wait_event.events = 0;
Olivier Houchardd54ede72021-03-05 23:42:41 +0100168
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200169 ctx->xprt = NULL;
170 ctx->xprt_ctx = NULL;
Willy Tarreauac6febd2020-01-10 09:08:22 +0100171 ctx->subs = NULL;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200172 *xprt_ctx = ctx;
173
174 return 0;
175}
176
177static void xprt_handshake_close(struct connection *conn, void *xprt_ctx)
178{
179 struct xprt_handshake_ctx *ctx = xprt_ctx;
180
181 if (ctx) {
182 if (ctx->wait_event.events != 0)
183 ctx->xprt->unsubscribe(ctx->conn, ctx->xprt_ctx,
184 ctx->wait_event.events,
185 &ctx->wait_event);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100186 if (ctx->subs) {
187 ctx->subs->events = 0;
188 tasklet_wakeup(ctx->subs->tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200189 }
190
191 if (ctx->xprt && ctx->xprt->close)
192 ctx->xprt->close(conn, ctx->xprt_ctx);
193 /* Remove any handshake flag, and if we were the connection
194 * xprt, get back to XPRT_RAW. If we're here because we
195 * failed an outoging connection, it will be retried using
196 * the same struct connection, and as xprt_handshake is a bit
197 * magic, because it requires a call to add_xprt(), it's better
198 * to fallback to the original XPRT to re-initiate the
199 * connection.
200 */
Willy Tarreau4450b582020-01-23 15:23:13 +0100201 conn->flags &= ~CO_FL_HANDSHAKE;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200202 if (conn->xprt == xprt_get(XPRT_HANDSHAKE))
203 conn->xprt = xprt_get(XPRT_RAW);
Willy Tarreau3c39a7d2019-06-14 14:42:29 +0200204 tasklet_free(ctx->wait_event.tasklet);
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200205 pool_free(xprt_handshake_ctx_pool, ctx);
206 }
207}
208
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100209/* Called from the upper layer, to subscribe <es> to events <event_type>. The
210 * event subscriber <es> is not allowed to change from a previous call as long
211 * as at least one event is still subscribed. The <event_type> must only be a
212 * combination of SUB_RETRY_RECV and SUB_RETRY_SEND. It always returns 0.
213 */
214static int xprt_handshake_subscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es)
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200215{
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200216 struct xprt_handshake_ctx *ctx = xprt_ctx;
217
Willy Tarreauac6febd2020-01-10 09:08:22 +0100218 BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100219 BUG_ON(ctx->subs && ctx->subs != es);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100220
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100221 ctx->subs = es;
222 es->events |= event_type;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200223 return 0;
224
225}
226
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100227/* Called from the upper layer, to unsubscribe <es> from events <event_type>.
228 * The <es> pointer is not allowed to differ from the one passed to the
229 * subscribe() call. It always returns zero.
230 */
231static int xprt_handshake_unsubscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es)
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200232{
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200233 struct xprt_handshake_ctx *ctx = xprt_ctx;
234
Willy Tarreauac6febd2020-01-10 09:08:22 +0100235 BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV));
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100236 BUG_ON(ctx->subs && ctx->subs != es);
Willy Tarreauac6febd2020-01-10 09:08:22 +0100237
Willy Tarreauee1a6fc2020-01-17 07:52:13 +0100238 es->events &= ~event_type;
239 if (!es->events)
Willy Tarreauac6febd2020-01-10 09:08:22 +0100240 ctx->subs = NULL;
241
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200242 return 0;
243}
244
245/* Use the provided XPRT as an underlying XPRT, and provide the old one.
246 * Returns 0 on success, and non-zero on failure.
247 */
248static 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)
249{
250 struct xprt_handshake_ctx *ctx = xprt_ctx;
251
252 if (oldxprt_ops)
253 *oldxprt_ops = ctx->xprt;
254 if (oldxprt_ctx)
255 *oldxprt_ctx = ctx->xprt_ctx;
256 ctx->xprt = toadd_ops;
257 ctx->xprt_ctx = toadd_ctx;
Olivier Houchardd54ede72021-03-05 23:42:41 +0100258
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200259 return 0;
260}
261
262/* Remove the specified xprt. If if it our underlying XPRT, remove it and
263 * return 0, otherwise just call the remove_xprt method from the underlying
264 * XPRT.
265 */
266static int xprt_handshake_remove_xprt(struct connection *conn, void *xprt_ctx, void *toremove_ctx, const struct xprt_ops *newops, void *newctx)
267{
268 struct xprt_handshake_ctx *ctx = xprt_ctx;
269
270 if (ctx->xprt_ctx == toremove_ctx) {
271 ctx->xprt_ctx = newctx;
272 ctx->xprt = newops;
273 return 0;
274 }
275 return (ctx->xprt->remove_xprt(conn, ctx->xprt_ctx, toremove_ctx, newops, newctx));
276}
277
278struct xprt_ops xprt_handshake = {
279 .snd_buf = xprt_handshake_from_buf,
280 .rcv_buf = xprt_handshake_to_buf,
281 .subscribe = xprt_handshake_subscribe,
282 .unsubscribe = xprt_handshake_unsubscribe,
283 .remove_xprt = xprt_handshake_remove_xprt,
284 .add_xprt = xprt_handshake_add_xprt,
285 .init = xprt_handshake_init,
Olivier Houchardd54ede72021-03-05 23:42:41 +0100286 .start = xprt_handshake_start,
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200287 .close= xprt_handshake_close,
288 .rcv_pipe = NULL,
289 .snd_pipe = NULL,
290 .shutr = NULL,
291 .shutw = NULL,
292 .name = "HS",
293};
294
295__attribute__((constructor))
296static void __xprt_handshake_init(void)
297{
298 xprt_register(XPRT_HANDSHAKE, &xprt_handshake);
299}