2 * Copyright (C) 2017-2019 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
40 #include <gnutls/gnutls.h>
44 #define NBDKIT_API_VERSION 2
46 #include <nbdkit-plugin.h>
51 MODE_BASE64EXPORTNAME
,
53 static enum mode mode
= MODE_EXPORTNAME
;
56 reflection_config (const char *key
, const char *value
)
58 if (strcmp (key
, "mode") == 0) {
59 if (strcasecmp (value
, "exportname") == 0 ||
60 strcasecmp (value
, "export-name") == 0) {
61 mode
= MODE_EXPORTNAME
;
63 else if (strcasecmp (value
, "base64exportname") == 0 ||
64 strcasecmp (value
, "base64-export-name") == 0) {
66 mode
= MODE_BASE64EXPORTNAME
;
68 nbdkit_error ("the plugin was compiled without base64 support");
73 nbdkit_error ("unknown mode: '%s'", value
);
78 nbdkit_error ("unknown parameter '%s'", key
);
85 #define reflection_config_help \
86 "mode=exportname|base64exportname Plugin mode."
88 /* Provide a way to detect if the base64 feature is supported. */
90 reflection_dump_plugin (void)
93 printf ("reflection_base64=yes\n");
97 /* Per-connection handle. */
99 void *data
; /* Block device data. */
100 size_t len
; /* Length of data in bytes. */
104 decode_base64 (const char *data
, size_t len
, struct handle
*ret
)
107 gnutls_datum_t in
, out
;
110 /* For unclear reasons gnutls_base64_decode2 won't handle an empty
111 * string, even though base64("") == "".
112 * https://tools.ietf.org/html/rfc4648#section-10
113 * https://gitlab.com/gnutls/gnutls/issues/834
114 * So we have to special-case it.
122 in
.data
= (unsigned char *) data
;
124 err
= gnutls_base64_decode2 (&in
, &out
);
125 if (err
!= GNUTLS_E_SUCCESS
) {
126 nbdkit_error ("base64: %s", gnutls_strerror (err
));
127 /* We don't have to free out.data. I verified that it is freed on
128 * the error path of gnutls_base64_decode2.
133 ret
->data
= out
.data
; /* caller frees, eventually */
137 nbdkit_error ("the plugin was compiled without base64 support");
142 /* Create the per-connection handle.
144 * This is a rather unusual plugin because it has to parse data sent
145 * by the client. For security reasons, be careful about:
147 * - Returning more data than is sent by the client.
149 * - Inputs that result in unbounded output.
151 * - Inputs that could hang, crash or exploit the server.
154 reflection_open (int readonly
)
156 const char *export_name
;
157 size_t export_name_len
;
160 h
= malloc (sizeof *h
);
162 nbdkit_error ("malloc: %m");
167 case MODE_EXPORTNAME
:
168 case MODE_BASE64EXPORTNAME
:
169 export_name
= nbdkit_export_name ();
170 if (export_name
== NULL
) {
174 export_name_len
= strlen (export_name
);
176 if (mode
== MODE_EXPORTNAME
) {
177 h
->len
= export_name_len
;
178 h
->data
= strdup (export_name
);
179 if (h
->data
== NULL
) {
180 nbdkit_error ("strdup: %m");
186 else /* mode == MODE_BASE64EXPORTNAME */ {
187 if (decode_base64 (export_name
, export_name_len
, h
) == -1) {
199 /* Close the per-connection handle. */
201 reflection_close (void *handle
)
203 struct handle
*h
= handle
;
209 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
211 /* Get the disk size. */
213 reflection_get_size (void *handle
)
215 struct handle
*h
= handle
;
217 return (int64_t) h
->len
;
220 /* Read-only plugin so multi-conn is safe. */
222 reflection_can_multi_conn (void *handle
)
229 reflection_can_cache (void *handle
)
231 /* Everything is already in memory, returning this without
232 * implementing .cache lets nbdkit do the correct no-op.
234 return NBDKIT_CACHE_NATIVE
;
239 reflection_pread (void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
242 struct handle
*h
= handle
;
244 memcpy (buf
, h
->data
+ offset
, count
);
248 static struct nbdkit_plugin plugin
= {
249 .name
= "reflection",
250 .version
= PACKAGE_VERSION
,
251 .config
= reflection_config
,
252 .config_help
= reflection_config_help
,
253 .dump_plugin
= reflection_dump_plugin
,
254 .magic_config_key
= "mode",
255 .open
= reflection_open
,
256 .close
= reflection_close
,
257 .get_size
= reflection_get_size
,
258 .can_multi_conn
= reflection_can_multi_conn
,
259 .can_cache
= reflection_can_cache
,
260 .pread
= reflection_pread
,
261 /* In this plugin, errno is preserved properly along error return
262 * paths from failed system calls.
264 .errno_is_preserved
= 1,
267 NBDKIT_REGISTER_PLUGIN(plugin
)