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
40 #ifdef HAVE_SYS_SOCKET_H
41 #include <sys/socket.h>
44 #ifdef HAVE_NETINET_IN_H
45 #include <netinet/in.h>
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
52 #if defined (HAVE_GNUTLS) && defined (HAVE_GNUTLS_BASE64_DECODE2)
53 #include <gnutls/gnutls.h>
57 #define NBDKIT_API_VERSION 2
59 #include <nbdkit-plugin.h>
61 #include "ascii-string.h"
62 #include "byte-swapping.h"
68 MODE_BASE64EXPORTNAME
,
74 static enum mode mode
= MODE_EXPORTNAME
;
76 /* Plugin load time. */
77 static struct timeval load_t
;
82 gettimeofday (&load_t
, NULL
);
86 info_config (const char *key
, const char *value
)
88 if (strcmp (key
, "mode") == 0) {
89 if (ascii_strcasecmp (value
, "exportname") == 0 ||
90 ascii_strcasecmp (value
, "export-name") == 0) {
91 mode
= MODE_EXPORTNAME
;
93 else if (ascii_strcasecmp (value
, "base64exportname") == 0 ||
94 ascii_strcasecmp (value
, "base64-export-name") == 0) {
96 mode
= MODE_BASE64EXPORTNAME
;
98 nbdkit_error ("the plugin was compiled without base64 support");
102 else if (ascii_strcasecmp (value
, "address") == 0) {
103 #ifdef HAVE_INET_NTOP
106 nbdkit_error ("the plugin was compiled without inet_ntop");
110 else if (ascii_strcasecmp (value
, "time") == 0)
112 else if (ascii_strcasecmp (value
, "uptime") == 0)
114 else if (ascii_strcasecmp (value
, "conntime") == 0)
115 mode
= MODE_CONNTIME
;
117 nbdkit_error ("unknown mode: '%s'", value
);
122 nbdkit_error ("unknown parameter '%s'", key
);
129 #define info_config_help \
130 "mode=exportname|base64exportname|address|time|uptime|conntime\n" \
131 " Plugin mode (default exportname)."
133 /* Provide a way to detect if the base64 feature is supported. */
135 info_dump_plugin (void)
137 #ifdef HAVE_INET_NTOP
138 printf ("info_address=yes\n");
141 printf ("info_base64=yes\n");
145 /* Per-connection handle. */
147 void *data
; /* Block device data. */
148 size_t len
; /* Length of data in bytes. */
149 struct timeval conn_t
; /* Time since connection was opened. */
153 decode_base64 (const char *data
, size_t len
, struct handle
*ret
)
156 gnutls_datum_t in
, out
;
159 /* For unclear reasons gnutls_base64_decode2 won't handle an empty
160 * string, even though base64("") == "".
161 * https://tools.ietf.org/html/rfc4648#section-10
162 * https://gitlab.com/gnutls/gnutls/issues/834
163 * So we have to special-case it.
171 in
.data
= (unsigned char *) data
;
173 err
= gnutls_base64_decode2 (&in
, &out
);
174 if (err
!= GNUTLS_E_SUCCESS
) {
175 nbdkit_error ("base64: %s", gnutls_strerror (err
));
176 /* We don't have to free out.data. I verified that it is freed on
177 * the error path of gnutls_base64_decode2.
182 ret
->data
= out
.data
; /* caller frees, eventually */
186 nbdkit_error ("the plugin was compiled without base64 support");
192 handle_address (struct sockaddr
*sa
, socklen_t addrlen
,
195 #ifdef HAVE_INET_NTOP
196 struct sockaddr_in
*addr
= (struct sockaddr_in
*) sa
;
197 struct sockaddr_in6
*addr6
= (struct sockaddr_in6
*) sa
;
199 char straddr
[INET_ADDRSTRLEN
];
200 char straddr6
[INET6_ADDRSTRLEN
];
205 switch (addr
->sin_family
) {
207 if (inet_ntop (AF_INET
, &addr
->sin_addr
,
208 u
.straddr
, sizeof u
.straddr
) == NULL
) {
209 nbdkit_error ("inet_ntop: %m");
212 r
= asprintf (&str
, "%s:%d", u
.straddr
, ntohs (addr
->sin_port
));
214 nbdkit_error ("asprintf: %m");
222 if (inet_ntop (AF_INET6
, &addr6
->sin6_addr
,
223 u
.straddr6
, sizeof u
.straddr6
) == NULL
) {
224 nbdkit_error ("inet_ntop: %m");
227 r
= asprintf (&str
, "[%s]:%d", u
.straddr6
, ntohs (addr6
->sin6_port
));
229 nbdkit_error ("asprintf: %m");
237 /* We don't want to expose the socket path because it's a host
238 * filesystem name. The client might not really be running on the
239 * same machine (eg. it is using a proxy). However it doesn't
240 * even matter because getpeername(2) on Linux returns a zero
241 * length sun_path in this case!
243 str
= strdup ("unix");
245 nbdkit_error ("strdup: %m");
248 ret
->len
= strlen (str
);
253 nbdkit_debug ("unsupported socket family %d", addr
->sin_family
);
259 nbdkit_error ("the plugin was compiled without inet_ntop");
264 /* Create the per-connection handle.
266 * This is a rather unusual plugin because it has to parse data sent
267 * by the client. For security reasons, be careful about:
269 * - Returning more data than is sent by the client.
271 * - Inputs that result in unbounded output.
273 * - Inputs that could hang, crash or exploit the server.
275 * - Leaking host information (eg. paths).
278 info_open (int readonly
)
280 const char *export_name
;
281 size_t export_name_len
;
282 struct sockaddr_storage addr
;
286 h
= malloc (sizeof *h
);
288 nbdkit_error ("malloc: %m");
293 case MODE_EXPORTNAME
:
294 case MODE_BASE64EXPORTNAME
:
295 export_name
= nbdkit_export_name ();
296 if (export_name
== NULL
) {
300 export_name_len
= strlen (export_name
);
302 if (mode
== MODE_EXPORTNAME
) {
303 h
->len
= export_name_len
;
304 h
->data
= strdup (export_name
);
305 if (h
->data
== NULL
) {
306 nbdkit_error ("strdup: %m");
312 else /* mode == MODE_BASE64EXPORTNAME */ {
313 if (decode_base64 (export_name
, export_name_len
, h
) == -1) {
321 addrlen
= sizeof addr
;
322 if (nbdkit_peer_name ((struct sockaddr
*) &addr
, &addrlen
) == -1 ||
323 handle_address ((struct sockaddr
*) &addr
, addrlen
, h
) == -1) {
332 gettimeofday (&h
->conn_t
, NULL
);
334 h
->data
= malloc (h
->len
);
335 if (h
->data
== NULL
) {
336 nbdkit_error ("malloc: %m");
347 /* Close the per-connection handle. */
349 info_close (void *handle
)
351 struct handle
*h
= handle
;
357 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
359 /* Get the disk size. */
361 info_get_size (void *handle
)
363 struct handle
*h
= handle
;
365 return (int64_t) h
->len
;
369 info_can_multi_conn (void *handle
)
372 /* Safe for exportname modes since clients should only request
373 * multi-conn with the same export name.
375 case MODE_EXPORTNAME
:
376 case MODE_BASE64EXPORTNAME
:
378 /* Unsafe for mode=address because all multi-conn connections
379 * won't necessarily originate from the same client address.
383 /* All time modes will read different values at different times,
384 * so all of them are unsafe for multi-conn.
391 /* Keep GCC happy. */
399 info_can_cache (void *handle
)
401 /* Everything is already in memory, returning this without
402 * implementing .cache lets nbdkit do the correct no-op.
404 return NBDKIT_CACHE_NATIVE
;
408 update_time (struct handle
*h
)
415 gettimeofday (&tv
, NULL
);
422 subtract_timeval (&load_t
, &tv
, &tv
);
426 subtract_timeval (&h
->conn_t
, &tv
, &tv
);
433 /* Pack the result into the output buffer. */
436 secs
= htobe64 (secs
);
437 usecs
= htobe32 (usecs
);
439 memcpy (&p
[0], &secs
, 8);
440 memcpy (&p
[8], &usecs
, 4);
445 info_pread (void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
448 struct handle
*h
= handle
;
450 /* For the time modes we update the data on every read. */
451 if (mode
== MODE_TIME
|| mode
== MODE_UPTIME
|| mode
== MODE_CONNTIME
)
454 memcpy (buf
, h
->data
+ offset
, count
);
458 static struct nbdkit_plugin plugin
= {
460 .version
= PACKAGE_VERSION
,
462 .config
= info_config
,
463 .config_help
= info_config_help
,
464 .dump_plugin
= info_dump_plugin
,
465 .magic_config_key
= "mode",
468 .get_size
= info_get_size
,
469 .can_multi_conn
= info_can_multi_conn
,
470 .can_cache
= info_can_cache
,
472 /* In this plugin, errno is preserved properly along error return
473 * paths from failed system calls.
475 .errno_is_preserved
= 1,
478 NBDKIT_REGISTER_PLUGIN (plugin
)