nbd: Send message along with server NBD_REP_ERR errors

The NBD Protocol allows us to send human-readable messages
along with any NBD_REP_ERR error during option negotiation;
make use of this fact for clients that know what to do with
our message.

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1476469998-28592-8-git-send-email-eblake@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Eric Blake 2016-10-14 13:33:09 -05:00 committed by Paolo Bonzini
parent 526e5c6559
commit 3668328303
1 changed files with 59 additions and 19 deletions

View File

@ -236,6 +236,38 @@ static int nbd_negotiate_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt)
return nbd_negotiate_send_rep_len(ioc, type, opt, 0); return nbd_negotiate_send_rep_len(ioc, type, opt, 0);
} }
/* Send an error reply.
* Return -errno on error, 0 on success. */
static int GCC_FMT_ATTR(4, 5)
nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type,
uint32_t opt, const char *fmt, ...)
{
va_list va;
char *msg;
int ret;
size_t len;
va_start(va, fmt);
msg = g_strdup_vprintf(fmt, va);
va_end(va);
len = strlen(msg);
assert(len < 4096);
TRACE("sending error message \"%s\"", msg);
ret = nbd_negotiate_send_rep_len(ioc, type, opt, len);
if (ret < 0) {
goto out;
}
if (nbd_negotiate_write(ioc, msg, len) != len) {
LOG("write failed (error message)");
ret = -EIO;
} else {
ret = 0;
}
out:
g_free(msg);
return ret;
}
/* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload. /* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload.
* Return -errno on error, 0 on success. */ * Return -errno on error, 0 on success. */
static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp) static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
@ -281,8 +313,9 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
if (nbd_negotiate_drop_sync(client->ioc, length) != length) { if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
return -EIO; return -EIO;
} }
return nbd_negotiate_send_rep(client->ioc, return nbd_negotiate_send_rep_err(client->ioc,
NBD_REP_ERR_INVALID, NBD_OPT_LIST); NBD_REP_ERR_INVALID, NBD_OPT_LIST,
"OPT_LIST should not have length");
} }
/* For each export, send a NBD_REP_SERVER reply. */ /* For each export, send a NBD_REP_SERVER reply. */
@ -329,7 +362,8 @@ fail:
return rc; return rc;
} }
/* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the
* new channel for all further (now-encrypted) communication. */
static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
uint32_t length) uint32_t length)
{ {
@ -343,7 +377,8 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
if (nbd_negotiate_drop_sync(ioc, length) != length) { if (nbd_negotiate_drop_sync(ioc, length) != length) {
return NULL; return NULL;
} }
nbd_negotiate_send_rep(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS); nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS,
"OPT_STARTTLS should not have length");
return NULL; return NULL;
} }
@ -474,13 +509,15 @@ static int nbd_negotiate_options(NBDClient *client)
return -EINVAL; return -EINVAL;
default: default:
TRACE("Option 0x%" PRIx32 " not permitted before TLS",
clientflags);
if (nbd_negotiate_drop_sync(client->ioc, length) != length) { if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
return -EIO; return -EIO;
} }
ret = nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_TLS_REQD, ret = nbd_negotiate_send_rep_err(client->ioc,
clientflags); NBD_REP_ERR_TLS_REQD,
clientflags,
"Option 0x%" PRIx32
"not permitted before TLS",
clientflags);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -506,27 +543,30 @@ static int nbd_negotiate_options(NBDClient *client)
return -EIO; return -EIO;
} }
if (client->tlscreds) { if (client->tlscreds) {
TRACE("TLS already enabled"); ret = nbd_negotiate_send_rep_err(client->ioc,
ret = nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_INVALID,
NBD_REP_ERR_INVALID, clientflags,
clientflags); "TLS already enabled");
} else { } else {
TRACE("TLS not configured"); ret = nbd_negotiate_send_rep_err(client->ioc,
ret = nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_POLICY,
NBD_REP_ERR_POLICY, clientflags,
clientflags); "TLS not configured");
} }
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
break; break;
default: default:
TRACE("Unsupported option 0x%" PRIx32, clientflags);
if (nbd_negotiate_drop_sync(client->ioc, length) != length) { if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
return -EIO; return -EIO;
} }
ret = nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP, ret = nbd_negotiate_send_rep_err(client->ioc,
clientflags); NBD_REP_ERR_UNSUP,
clientflags,
"Unsupported option 0x%"
PRIx32,
clientflags);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }