aboutsummaryrefslogtreecommitdiff
path: root/lib/libiscsiutil
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libiscsiutil')
-rw-r--r--lib/libiscsiutil/Makefile9
-rw-r--r--lib/libiscsiutil/Makefile.depend14
-rw-r--r--lib/libiscsiutil/chap.c419
-rw-r--r--lib/libiscsiutil/connection.c53
-rw-r--r--lib/libiscsiutil/keys.c191
-rw-r--r--lib/libiscsiutil/libiscsiutil.h181
-rw-r--r--lib/libiscsiutil/log.c220
-rw-r--r--lib/libiscsiutil/pdu.c225
-rw-r--r--lib/libiscsiutil/text.c333
-rw-r--r--lib/libiscsiutil/utils.c115
10 files changed, 1760 insertions, 0 deletions
diff --git a/lib/libiscsiutil/Makefile b/lib/libiscsiutil/Makefile
new file mode 100644
index 000000000000..d9762302fd40
--- /dev/null
+++ b/lib/libiscsiutil/Makefile
@@ -0,0 +1,9 @@
+LIB= iscsiutil
+INTERNALLIB=
+
+INCS= libiscsiutil.h
+
+SRCS= chap.c connection.c keys.c log.c pdu.c text.c utils.c
+CFLAGS+= -I${SRCTOP}/sys/dev/iscsi
+
+.include <bsd.lib.mk>
diff --git a/lib/libiscsiutil/Makefile.depend b/lib/libiscsiutil/Makefile.depend
new file mode 100644
index 000000000000..e1cc198eab19
--- /dev/null
+++ b/lib/libiscsiutil/Makefile.depend
@@ -0,0 +1,14 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/libmd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libiscsiutil/chap.c b/lib/libiscsiutil/chap.c
new file mode 100644
index 000000000000..90fce9683487
--- /dev/null
+++ b/lib/libiscsiutil/chap.c
@@ -0,0 +1,419 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2014 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <md5.h>
+
+#include "libiscsiutil.h"
+
+static void
+chap_compute_md5(const char id, const char *secret,
+ const void *challenge, size_t challenge_len, void *response,
+ size_t response_len)
+{
+ MD5_CTX ctx;
+
+ assert(response_len == CHAP_DIGEST_LEN);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, &id, sizeof(id));
+ MD5Update(&ctx, secret, strlen(secret));
+ MD5Update(&ctx, challenge, challenge_len);
+ MD5Final(response, &ctx);
+}
+
+static int
+chap_hex2int(const char hex)
+{
+ switch (hex) {
+ case '0':
+ return (0x00);
+ case '1':
+ return (0x01);
+ case '2':
+ return (0x02);
+ case '3':
+ return (0x03);
+ case '4':
+ return (0x04);
+ case '5':
+ return (0x05);
+ case '6':
+ return (0x06);
+ case '7':
+ return (0x07);
+ case '8':
+ return (0x08);
+ case '9':
+ return (0x09);
+ case 'a':
+ case 'A':
+ return (0x0a);
+ case 'b':
+ case 'B':
+ return (0x0b);
+ case 'c':
+ case 'C':
+ return (0x0c);
+ case 'd':
+ case 'D':
+ return (0x0d);
+ case 'e':
+ case 'E':
+ return (0x0e);
+ case 'f':
+ case 'F':
+ return (0x0f);
+ default:
+ return (-1);
+ }
+}
+
+static int
+chap_b642bin(const char *b64, void **binp, size_t *bin_lenp)
+{
+ char *bin;
+ int b64_len, bin_len;
+
+ b64_len = strlen(b64);
+ bin_len = (b64_len + 3) / 4 * 3;
+ bin = calloc(bin_len, 1);
+ if (bin == NULL)
+ log_err(1, "calloc");
+
+ bin_len = b64_pton(b64, bin, bin_len);
+ if (bin_len < 0) {
+ log_warnx("malformed base64 variable");
+ free(bin);
+ return (-1);
+ }
+ *binp = bin;
+ *bin_lenp = bin_len;
+ return (0);
+}
+
+/*
+ * XXX: Review this _carefully_.
+ */
+static int
+chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp)
+{
+ int i, hex_len, nibble;
+ bool lo = true; /* As opposed to 'hi'. */
+ char *bin;
+ size_t bin_off, bin_len;
+
+ if (strncasecmp(hex, "0b", strlen("0b")) == 0)
+ return (chap_b642bin(hex + 2, binp, bin_lenp));
+
+ if (strncasecmp(hex, "0x", strlen("0x")) != 0) {
+ log_warnx("malformed variable, should start with \"0x\""
+ " or \"0b\"");
+ return (-1);
+ }
+
+ hex += strlen("0x");
+ hex_len = strlen(hex);
+ if (hex_len < 1) {
+ log_warnx("malformed variable; doesn't contain anything "
+ "but \"0x\"");
+ return (-1);
+ }
+
+ bin_len = hex_len / 2 + hex_len % 2;
+ bin = calloc(bin_len, 1);
+ if (bin == NULL)
+ log_err(1, "calloc");
+
+ bin_off = bin_len - 1;
+ for (i = hex_len - 1; i >= 0; i--) {
+ nibble = chap_hex2int(hex[i]);
+ if (nibble < 0) {
+ log_warnx("malformed variable, invalid char \"%c\"",
+ hex[i]);
+ free(bin);
+ return (-1);
+ }
+
+ assert(bin_off < bin_len);
+ if (lo) {
+ bin[bin_off] = nibble;
+ lo = false;
+ } else {
+ bin[bin_off] |= nibble << 4;
+ bin_off--;
+ lo = true;
+ }
+ }
+
+ *binp = bin;
+ *bin_lenp = bin_len;
+ return (0);
+}
+
+#ifdef USE_BASE64
+static char *
+chap_bin2hex(const char *bin, size_t bin_len)
+{
+ unsigned char *b64, *tmp;
+ size_t b64_len;
+
+ b64_len = (bin_len + 2) / 3 * 4 + 3; /* +2 for "0b", +1 for '\0'. */
+ b64 = malloc(b64_len);
+ if (b64 == NULL)
+ log_err(1, "malloc");
+
+ tmp = b64;
+ tmp += sprintf(tmp, "0b");
+ b64_ntop(bin, bin_len, tmp, b64_len - 2);
+
+ return (b64);
+}
+#else
+static char *
+chap_bin2hex(const char *bin, size_t bin_len)
+{
+ unsigned char *hex, *tmp, ch;
+ size_t hex_len;
+ size_t i;
+
+ hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */
+ hex = malloc(hex_len);
+ if (hex == NULL)
+ log_err(1, "malloc");
+
+ tmp = hex;
+ tmp += sprintf(tmp, "0x");
+ for (i = 0; i < bin_len; i++) {
+ ch = bin[i];
+ tmp += sprintf(tmp, "%02x", ch);
+ }
+
+ return (hex);
+}
+#endif /* !USE_BASE64 */
+
+struct chap *
+chap_new(void)
+{
+ struct chap *chap;
+
+ chap = calloc(1, sizeof(*chap));
+ if (chap == NULL)
+ log_err(1, "calloc");
+
+ /*
+ * Generate the challenge.
+ */
+ arc4random_buf(chap->chap_challenge, sizeof(chap->chap_challenge));
+ arc4random_buf(&chap->chap_id, sizeof(chap->chap_id));
+
+ return (chap);
+}
+
+char *
+chap_get_id(const struct chap *chap)
+{
+ char *chap_i;
+ int ret;
+
+ ret = asprintf(&chap_i, "%d", chap->chap_id);
+ if (ret < 0)
+ log_err(1, "asprintf");
+
+ return (chap_i);
+}
+
+char *
+chap_get_challenge(const struct chap *chap)
+{
+ char *chap_c;
+
+ chap_c = chap_bin2hex(chap->chap_challenge,
+ sizeof(chap->chap_challenge));
+
+ return (chap_c);
+}
+
+static int
+chap_receive_bin(struct chap *chap, void *response, size_t response_len)
+{
+
+ if (response_len != sizeof(chap->chap_response)) {
+ log_debugx("got CHAP response with invalid length; "
+ "got %zd, should be %zd",
+ response_len, sizeof(chap->chap_response));
+ return (1);
+ }
+
+ memcpy(chap->chap_response, response, response_len);
+ return (0);
+}
+
+int
+chap_receive(struct chap *chap, const char *response)
+{
+ void *response_bin;
+ size_t response_bin_len;
+ int error;
+
+ error = chap_hex2bin(response, &response_bin, &response_bin_len);
+ if (error != 0) {
+ log_debugx("got incorrectly encoded CHAP response \"%s\"",
+ response);
+ return (1);
+ }
+
+ error = chap_receive_bin(chap, response_bin, response_bin_len);
+ free(response_bin);
+
+ return (error);
+}
+
+int
+chap_authenticate(struct chap *chap, const char *secret)
+{
+ char expected_response[CHAP_DIGEST_LEN];
+
+ chap_compute_md5(chap->chap_id, secret,
+ chap->chap_challenge, sizeof(chap->chap_challenge),
+ expected_response, sizeof(expected_response));
+
+ if (memcmp(chap->chap_response,
+ expected_response, sizeof(expected_response)) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+chap_delete(struct chap *chap)
+{
+
+ free(chap);
+}
+
+struct rchap *
+rchap_new(const char *secret)
+{
+ struct rchap *rchap;
+
+ rchap = calloc(1, sizeof(*rchap));
+ if (rchap == NULL)
+ log_err(1, "calloc");
+
+ rchap->rchap_secret = checked_strdup(secret);
+
+ return (rchap);
+}
+
+static void
+rchap_receive_bin(struct rchap *rchap, const unsigned char id,
+ const void *challenge, size_t challenge_len)
+{
+
+ rchap->rchap_id = id;
+ rchap->rchap_challenge = calloc(challenge_len, 1);
+ if (rchap->rchap_challenge == NULL)
+ log_err(1, "calloc");
+ memcpy(rchap->rchap_challenge, challenge, challenge_len);
+ rchap->rchap_challenge_len = challenge_len;
+}
+
+int
+rchap_receive(struct rchap *rchap, const char *id, const char *challenge)
+{
+ unsigned char id_bin;
+ void *challenge_bin;
+ size_t challenge_bin_len;
+
+ int error;
+
+ id_bin = strtoul(id, NULL, 10);
+
+ error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len);
+ if (error != 0) {
+ log_debugx("got incorrectly encoded CHAP challenge \"%s\"",
+ challenge);
+ return (1);
+ }
+
+ rchap_receive_bin(rchap, id_bin, challenge_bin, challenge_bin_len);
+ free(challenge_bin);
+
+ return (0);
+}
+
+static void
+rchap_get_response_bin(struct rchap *rchap,
+ void **responsep, size_t *response_lenp)
+{
+ void *response_bin;
+ size_t response_bin_len = CHAP_DIGEST_LEN;
+
+ response_bin = calloc(response_bin_len, 1);
+ if (response_bin == NULL)
+ log_err(1, "calloc");
+
+ chap_compute_md5(rchap->rchap_id, rchap->rchap_secret,
+ rchap->rchap_challenge, rchap->rchap_challenge_len,
+ response_bin, response_bin_len);
+
+ *responsep = response_bin;
+ *response_lenp = response_bin_len;
+}
+
+char *
+rchap_get_response(struct rchap *rchap)
+{
+ void *response;
+ size_t response_len;
+ char *chap_r;
+
+ rchap_get_response_bin(rchap, &response, &response_len);
+ chap_r = chap_bin2hex(response, response_len);
+ free(response);
+
+ return (chap_r);
+}
+
+void
+rchap_delete(struct rchap *rchap)
+{
+
+ free(rchap->rchap_secret);
+ free(rchap->rchap_challenge);
+ free(rchap);
+}
diff --git a/lib/libiscsiutil/connection.c b/lib/libiscsiutil/connection.c
new file mode 100644
index 000000000000..0fbf0c95e30e
--- /dev/null
+++ b/lib/libiscsiutil/connection.c
@@ -0,0 +1,53 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include "libiscsiutil.h"
+
+void
+connection_init(struct connection *conn, const struct connection_ops *ops,
+ bool use_proxy)
+{
+ memset(conn, 0, sizeof(*conn));
+ conn->conn_ops = ops;
+ conn->conn_use_proxy = use_proxy;
+
+ /*
+ * Default values, from RFC 3720, section 12.
+ */
+ conn->conn_header_digest = CONN_DIGEST_NONE;
+ conn->conn_data_digest = CONN_DIGEST_NONE;
+ conn->conn_immediate_data = true;
+ conn->conn_max_recv_data_segment_length = 8192;
+ conn->conn_max_send_data_segment_length = 8192;
+ conn->conn_max_burst_length = 262144;
+ conn->conn_first_burst_length = 65536;
+}
diff --git a/lib/libiscsiutil/keys.c b/lib/libiscsiutil/keys.c
new file mode 100644
index 000000000000..e0529d1ff19d
--- /dev/null
+++ b/lib/libiscsiutil/keys.c
@@ -0,0 +1,191 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libiscsiutil.h"
+
+struct keys *
+keys_new(void)
+{
+ struct keys *keys;
+
+ keys = calloc(1, sizeof(*keys));
+ if (keys == NULL)
+ log_err(1, "calloc");
+
+ return (keys);
+}
+
+void
+keys_delete(struct keys *keys)
+{
+
+ for (int i = 0; i < KEYS_MAX; i++) {
+ free(keys->keys_names[i]);
+ free(keys->keys_values[i]);
+ }
+ free(keys);
+}
+
+void
+keys_load(struct keys *keys, const char *data, size_t len)
+{
+ int i;
+ char *keys_data, *name, *pair, *value;
+ size_t pair_len;
+
+ if (len == 0)
+ return;
+
+ if (data[len - 1] != '\0')
+ log_errx(1, "protocol error: key not NULL-terminated\n");
+
+ keys_data = malloc(len);
+ if (keys_data == NULL)
+ log_err(1, "malloc");
+ memcpy(keys_data, data, len);
+
+ /*
+ * XXX: Review this carefully.
+ */
+ pair = keys_data;
+ for (i = 0;; i++) {
+ if (i >= KEYS_MAX)
+ log_errx(1, "too many keys received");
+
+ pair_len = strlen(pair);
+
+ value = pair;
+ name = strsep(&value, "=");
+ if (name == NULL || value == NULL)
+ log_errx(1, "malformed keys");
+ keys->keys_names[i] = checked_strdup(name);
+ keys->keys_values[i] = checked_strdup(value);
+ log_debugx("key received: \"%s=%s\"",
+ keys->keys_names[i], keys->keys_values[i]);
+
+ pair += pair_len + 1; /* +1 to skip the terminating '\0'. */
+ if (pair == keys_data + len)
+ break;
+ assert(pair < keys_data + len);
+ }
+ free(keys_data);
+}
+
+void
+keys_save(struct keys *keys, char **datap, size_t *lenp)
+{
+ FILE *fp;
+ char *data;
+ size_t len;
+ int i;
+
+ fp = open_memstream(&data, &len);
+ if (fp == NULL)
+ log_err(1, "open_memstream");
+ for (i = 0; i < KEYS_MAX; i++) {
+ if (keys->keys_names[i] == NULL)
+ break;
+
+ fprintf(fp, "%s=%s", keys->keys_names[i], keys->keys_values[i]);
+
+ /* Append a '\0' after each key pair. */
+ fputc('\0', fp);
+ }
+ if (fclose(fp) != 0)
+ log_err(1, "fclose");
+
+ if (len == 0) {
+ free(data);
+ data = NULL;
+ }
+
+ *datap = data;
+ *lenp = len;
+}
+
+const char *
+keys_find(struct keys *keys, const char *name)
+{
+ int i;
+
+ /*
+ * Note that we don't handle duplicated key names here,
+ * as they are not supposed to happen in requests, and if they do,
+ * it's an initiator error.
+ */
+ for (i = 0; i < KEYS_MAX; i++) {
+ if (keys->keys_names[i] == NULL)
+ return (NULL);
+ if (strcmp(keys->keys_names[i], name) == 0)
+ return (keys->keys_values[i]);
+ }
+ return (NULL);
+}
+
+void
+keys_add(struct keys *keys, const char *name, const char *value)
+{
+ int i;
+
+ log_debugx("key to send: \"%s=%s\"", name, value);
+
+ /*
+ * Note that we don't check for duplicates here, as they are perfectly
+ * fine in responses, e.g. the "TargetName" keys in discovery session
+ * response.
+ */
+ for (i = 0; i < KEYS_MAX; i++) {
+ if (keys->keys_names[i] == NULL) {
+ keys->keys_names[i] = checked_strdup(name);
+ keys->keys_values[i] = checked_strdup(value);
+ return;
+ }
+ }
+ log_errx(1, "too many keys");
+}
+
+void
+keys_add_int(struct keys *keys, const char *name, int value)
+{
+ char *str;
+ int ret;
+
+ ret = asprintf(&str, "%d", value);
+ if (ret <= 0)
+ log_err(1, "asprintf");
+
+ keys_add(keys, name, str);
+ free(str);
+}
diff --git a/lib/libiscsiutil/libiscsiutil.h b/lib/libiscsiutil/libiscsiutil.h
new file mode 100644
index 000000000000..b50afa1c9409
--- /dev/null
+++ b/lib/libiscsiutil/libiscsiutil.h
@@ -0,0 +1,181 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __LIBISCSIUTIL_H__
+#define __LIBISCSIUTIL_H__
+
+#include <sys/types.h>
+#include <stdbool.h>
+
+struct connection_ops;
+
+#define CONN_DIGEST_NONE 0
+#define CONN_DIGEST_CRC32C 1
+
+struct connection {
+ const struct connection_ops *conn_ops;
+ int conn_socket;
+ uint8_t conn_isid[6];
+ uint16_t conn_tsih;
+ uint32_t conn_cmdsn;
+ uint32_t conn_statsn;
+ int conn_header_digest;
+ int conn_data_digest;
+ bool conn_immediate_data;
+ bool conn_use_proxy;
+ int conn_max_recv_data_segment_length;
+ int conn_max_send_data_segment_length;
+ int conn_max_burst_length;
+ int conn_first_burst_length;
+ int conn_ping_timeout;
+ int conn_login_timeout;
+};
+
+struct pdu {
+ struct connection *pdu_connection;
+ struct iscsi_bhs *pdu_bhs;
+ char *pdu_data;
+ size_t pdu_data_len;
+};
+
+struct connection_ops {
+ bool (*timed_out)(void);
+ void (*pdu_receive_proxy)(struct pdu *);
+ void (*pdu_send_proxy)(struct pdu *);
+ void (*fail)(const struct connection *, const char *);
+};
+
+#define KEYS_MAX 1024
+
+struct keys {
+ char *keys_names[KEYS_MAX];
+ char *keys_values[KEYS_MAX];
+};
+
+#define CHAP_CHALLENGE_LEN 1024
+#define CHAP_DIGEST_LEN 16 /* Equal to MD5 digest size. */
+
+struct chap {
+ unsigned char chap_id;
+ char chap_challenge[CHAP_CHALLENGE_LEN];
+ char chap_response[CHAP_DIGEST_LEN];
+};
+
+struct rchap {
+ char *rchap_secret;
+ unsigned char rchap_id;
+ void *rchap_challenge;
+ size_t rchap_challenge_len;
+};
+
+__BEGIN_DECLS
+
+struct chap *chap_new(void);
+char *chap_get_id(const struct chap *chap);
+char *chap_get_challenge(const struct chap *chap);
+int chap_receive(struct chap *chap, const char *response);
+int chap_authenticate(struct chap *chap,
+ const char *secret);
+void chap_delete(struct chap *chap);
+
+struct rchap *rchap_new(const char *secret);
+int rchap_receive(struct rchap *rchap,
+ const char *id, const char *challenge);
+char *rchap_get_response(struct rchap *rchap);
+void rchap_delete(struct rchap *rchap);
+
+struct keys *keys_new(void);
+void keys_delete(struct keys *key);
+void keys_load(struct keys *keys, const char *data,
+ size_t len);
+void keys_save(struct keys *keys, char **datap,
+ size_t *lenp);
+const char *keys_find(struct keys *keys, const char *name);
+void keys_add(struct keys *keys,
+ const char *name, const char *value);
+void keys_add_int(struct keys *keys,
+ const char *name, int value);
+
+static __inline void
+keys_load_pdu(struct keys *keys, const struct pdu *pdu)
+{
+ keys_load(keys, pdu->pdu_data, pdu->pdu_data_len);
+}
+
+static __inline void
+keys_save_pdu(struct keys *keys, struct pdu *pdu)
+{
+ keys_save(keys, &pdu->pdu_data, &pdu->pdu_data_len);
+}
+
+struct pdu *pdu_new(struct connection *ic);
+struct pdu *pdu_new_response(struct pdu *request);
+int pdu_ahs_length(const struct pdu *pdu);
+int pdu_data_segment_length(const struct pdu *pdu);
+void pdu_set_data_segment_length(struct pdu *pdu,
+ uint32_t len);
+void pdu_receive(struct pdu *request);
+void pdu_send(struct pdu *response);
+void pdu_delete(struct pdu *ip);
+
+void text_send_request(struct connection *conn,
+ struct keys *request_keys);
+struct keys * text_read_response(struct connection *conn);
+struct keys * text_read_request(struct connection *conn,
+ struct pdu **requestp);
+void text_send_response(struct pdu *request,
+ struct keys *response_keys);
+
+void connection_init(struct connection *conn,
+ const struct connection_ops *ops, bool use_proxy);
+
+bool valid_iscsi_name(const char *name,
+ void (*warn_fn)(const char *, ...));
+
+void log_init(int level);
+void log_set_peer_name(const char *name);
+void log_set_peer_addr(const char *addr);
+void log_err(int, const char *, ...)
+ __dead2 __printflike(2, 3);
+void log_errc(int, int, const char *, ...)
+ __dead2 __printflike(3, 4);
+void log_errx(int, const char *, ...)
+ __dead2 __printflike(2, 3);
+void log_warn(const char *, ...) __printflike(1, 2);
+void log_warnc(int, const char *, ...)
+ __printflike(2, 3);
+void log_warnx(const char *, ...) __printflike(1, 2);
+void log_debugx(const char *, ...) __printflike(1, 2);
+
+char *checked_strdup(const char *);
+
+__END_DECLS
+
+#endif /* !__LIBISCSIUTIL_H__ */
diff --git a/lib/libiscsiutil/log.c b/lib/libiscsiutil/log.c
new file mode 100644
index 000000000000..cead4ab2d709
--- /dev/null
+++ b/lib/libiscsiutil/log.c
@@ -0,0 +1,220 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <vis.h>
+
+#include "libiscsiutil.h"
+
+static int log_level = 0;
+static char *peer_name = NULL;
+static char *peer_addr = NULL;
+
+#define MSGBUF_LEN 1024
+
+void
+log_init(int level)
+{
+
+ log_level = level;
+ openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON);
+}
+
+void
+log_set_peer_name(const char *name)
+{
+
+ /*
+ * XXX: Turn it into assertion?
+ */
+ if (peer_name != NULL)
+ log_errx(1, "%s called twice", __func__);
+ if (peer_addr == NULL)
+ log_errx(1, "%s called before log_set_peer_addr", __func__);
+
+ peer_name = checked_strdup(name);
+}
+
+void
+log_set_peer_addr(const char *addr)
+{
+
+ /*
+ * XXX: Turn it into assertion?
+ */
+ if (peer_addr != NULL)
+ log_errx(1, "%s called twice", __func__);
+
+ peer_addr = checked_strdup(addr);
+}
+
+static void
+log_common(int priority, int log_errno, const char *fmt, va_list ap)
+{
+ static char msgbuf[MSGBUF_LEN];
+ static char msgbuf_strvised[MSGBUF_LEN * 4 + 1];
+ char *errstr;
+ int ret;
+
+ ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ if (ret < 0) {
+ fprintf(stderr, "%s: snprintf failed", getprogname());
+ syslog(LOG_CRIT, "snprintf failed");
+ exit(1);
+ }
+
+ ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL);
+ if (ret < 0) {
+ fprintf(stderr, "%s: strnvis failed", getprogname());
+ syslog(LOG_CRIT, "strnvis failed");
+ exit(1);
+ }
+
+ if (log_errno == -1) {
+ if (peer_name != NULL) {
+ fprintf(stderr, "%s: %s (%s): %s\n", getprogname(),
+ peer_addr, peer_name, msgbuf_strvised);
+ syslog(priority, "%s (%s): %s",
+ peer_addr, peer_name, msgbuf_strvised);
+ } else if (peer_addr != NULL) {
+ fprintf(stderr, "%s: %s: %s\n", getprogname(),
+ peer_addr, msgbuf_strvised);
+ syslog(priority, "%s: %s",
+ peer_addr, msgbuf_strvised);
+ } else {
+ fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised);
+ syslog(priority, "%s", msgbuf_strvised);
+ }
+
+ } else {
+ errstr = strerror(log_errno);
+
+ if (peer_name != NULL) {
+ fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(),
+ peer_addr, peer_name, msgbuf_strvised, errstr);
+ syslog(priority, "%s (%s): %s: %s",
+ peer_addr, peer_name, msgbuf_strvised, errstr);
+ } else if (peer_addr != NULL) {
+ fprintf(stderr, "%s: %s: %s: %s\n", getprogname(),
+ peer_addr, msgbuf_strvised, errstr);
+ syslog(priority, "%s: %s: %s",
+ peer_addr, msgbuf_strvised, errstr);
+ } else {
+ fprintf(stderr, "%s: %s: %s\n", getprogname(),
+ msgbuf_strvised, errstr);
+ syslog(priority, "%s: %s",
+ msgbuf_strvised, errstr);
+ }
+ }
+}
+
+void
+log_err(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_CRIT, errno, fmt, ap);
+ va_end(ap);
+
+ exit(eval);
+}
+
+void
+log_errc(int eval, int code, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_CRIT, code, fmt, ap);
+ va_end(ap);
+
+ exit(eval);
+}
+
+void
+log_errx(int eval, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_CRIT, -1, fmt, ap);
+ va_end(ap);
+
+ exit(eval);
+}
+
+void
+log_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_WARNING, errno, fmt, ap);
+ va_end(ap);
+}
+
+void
+log_warnc(int code, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_WARNING, code, fmt, ap);
+ va_end(ap);
+}
+
+void
+log_warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_common(LOG_WARNING, -1, fmt, ap);
+ va_end(ap);
+}
+
+void
+log_debugx(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (log_level == 0)
+ return;
+
+ va_start(ap, fmt);
+ log_common(LOG_DEBUG, -1, fmt, ap);
+ va_end(ap);
+}
diff --git a/lib/libiscsiutil/pdu.c b/lib/libiscsiutil/pdu.c
new file mode 100644
index 000000000000..a26fc27ce2c8
--- /dev/null
+++ b/lib/libiscsiutil/pdu.c
@@ -0,0 +1,225 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <iscsi_proto.h>
+#include "libiscsiutil.h"
+
+int
+pdu_ahs_length(const struct pdu *pdu)
+{
+
+ return (pdu->pdu_bhs->bhs_total_ahs_len * 4);
+}
+
+int
+pdu_data_segment_length(const struct pdu *pdu)
+{
+ uint32_t len = 0;
+
+ len += pdu->pdu_bhs->bhs_data_segment_len[0];
+ len <<= 8;
+ len += pdu->pdu_bhs->bhs_data_segment_len[1];
+ len <<= 8;
+ len += pdu->pdu_bhs->bhs_data_segment_len[2];
+
+ return (len);
+}
+
+void
+pdu_set_data_segment_length(struct pdu *pdu, uint32_t len)
+{
+
+ pdu->pdu_bhs->bhs_data_segment_len[2] = len;
+ pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8;
+ pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16;
+}
+
+struct pdu *
+pdu_new(struct connection *conn)
+{
+ struct pdu *pdu;
+
+ pdu = calloc(1, sizeof(*pdu));
+ if (pdu == NULL)
+ log_err(1, "calloc");
+
+ pdu->pdu_bhs = calloc(1, sizeof(*pdu->pdu_bhs));
+ if (pdu->pdu_bhs == NULL)
+ log_err(1, "calloc");
+
+ pdu->pdu_connection = conn;
+
+ return (pdu);
+}
+
+struct pdu *
+pdu_new_response(struct pdu *request)
+{
+
+ return (pdu_new(request->pdu_connection));
+}
+
+static size_t
+pdu_padding(const struct pdu *pdu)
+{
+
+ if ((pdu->pdu_data_len % 4) != 0)
+ return (4 - (pdu->pdu_data_len % 4));
+
+ return (0);
+}
+
+static void
+pdu_read(const struct connection *conn, char *data, size_t len)
+{
+ ssize_t ret;
+
+ while (len > 0) {
+ ret = read(conn->conn_socket, data, len);
+ if (ret < 0) {
+ if (conn->conn_ops->timed_out()) {
+ conn->conn_ops->fail(conn,
+ "Login Phase timeout");
+ log_errx(1, "exiting due to timeout");
+ }
+ conn->conn_ops->fail(conn, strerror(errno));
+ log_err(1, "read");
+ } else if (ret == 0) {
+ conn->conn_ops->fail(conn, "connection lost");
+ log_errx(1, "read: connection lost");
+ }
+ len -= ret;
+ data += ret;
+ }
+}
+
+void
+pdu_receive(struct pdu *pdu)
+{
+ struct connection *conn;
+ size_t len, padding;
+ char dummy[4];
+
+ conn = pdu->pdu_connection;
+ if (conn->conn_use_proxy)
+ return (conn->conn_ops->pdu_receive_proxy(pdu));
+
+ pdu_read(conn, (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs));
+
+ len = pdu_ahs_length(pdu);
+ if (len > 0)
+ log_errx(1, "protocol error: non-empty AHS");
+
+ len = pdu_data_segment_length(pdu);
+ if (len > 0) {
+ if (len > (size_t)conn->conn_max_recv_data_segment_length) {
+ log_errx(1, "protocol error: received PDU "
+ "with DataSegmentLength exceeding %d",
+ conn->conn_max_recv_data_segment_length);
+ }
+
+ pdu->pdu_data_len = len;
+ pdu->pdu_data = malloc(len);
+ if (pdu->pdu_data == NULL)
+ log_err(1, "malloc");
+
+ pdu_read(conn, (char *)pdu->pdu_data, pdu->pdu_data_len);
+
+ padding = pdu_padding(pdu);
+ if (padding != 0) {
+ assert(padding < sizeof(dummy));
+ pdu_read(conn, (char *)dummy, padding);
+ }
+ }
+}
+
+void
+pdu_send(struct pdu *pdu)
+{
+ struct connection *conn;
+ ssize_t ret, total_len;
+ size_t padding;
+ uint32_t zero = 0;
+ struct iovec iov[3];
+ int iovcnt;
+
+ conn = pdu->pdu_connection;
+ if (conn->conn_use_proxy)
+ return (conn->conn_ops->pdu_send_proxy(pdu));
+
+ pdu_set_data_segment_length(pdu, pdu->pdu_data_len);
+ iov[0].iov_base = pdu->pdu_bhs;
+ iov[0].iov_len = sizeof(*pdu->pdu_bhs);
+ total_len = iov[0].iov_len;
+ iovcnt = 1;
+
+ if (pdu->pdu_data_len > 0) {
+ iov[1].iov_base = pdu->pdu_data;
+ iov[1].iov_len = pdu->pdu_data_len;
+ total_len += iov[1].iov_len;
+ iovcnt = 2;
+
+ padding = pdu_padding(pdu);
+ if (padding > 0) {
+ assert(padding < sizeof(zero));
+ iov[2].iov_base = &zero;
+ iov[2].iov_len = padding;
+ total_len += iov[2].iov_len;
+ iovcnt = 3;
+ }
+ }
+
+ ret = writev(conn->conn_socket, iov, iovcnt);
+ if (ret < 0) {
+ if (conn->conn_ops->timed_out())
+ log_errx(1, "exiting due to timeout");
+ log_err(1, "writev");
+ }
+ if (ret != total_len)
+ log_errx(1, "short write");
+}
+
+void
+pdu_delete(struct pdu *pdu)
+{
+
+ free(pdu->pdu_data);
+ free(pdu->pdu_bhs);
+ free(pdu);
+}
diff --git a/lib/libiscsiutil/text.c b/lib/libiscsiutil/text.c
new file mode 100644
index 000000000000..a4f587845005
--- /dev/null
+++ b/lib/libiscsiutil/text.c
@@ -0,0 +1,333 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <iscsi_proto.h>
+#include "libiscsiutil.h"
+
+/* Construct a new TextRequest PDU. */
+static struct pdu *
+text_new_request(struct connection *conn, uint32_t ttt)
+{
+ struct pdu *request;
+ struct iscsi_bhs_text_request *bhstr;
+
+ request = pdu_new(conn);
+ bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
+ bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST |
+ ISCSI_BHS_OPCODE_IMMEDIATE;
+ bhstr->bhstr_flags = BHSTR_FLAGS_FINAL;
+ bhstr->bhstr_initiator_task_tag = 0;
+ bhstr->bhstr_target_transfer_tag = ttt;
+
+ bhstr->bhstr_cmdsn = conn->conn_cmdsn;
+ bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1);
+
+ return (request);
+}
+
+/* Receive a TextRequest PDU from a connection. */
+static struct pdu *
+text_receive_request(struct connection *conn)
+{
+ struct pdu *request;
+ struct iscsi_bhs_text_request *bhstr;
+
+ request = pdu_new(conn);
+ pdu_receive(request);
+ if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
+ ISCSI_BHS_OPCODE_TEXT_REQUEST)
+ log_errx(1, "protocol error: received invalid opcode 0x%x",
+ request->pdu_bhs->bhs_opcode);
+ bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
+
+ /*
+ * XXX: Implement the C flag some day.
+ */
+ if ((bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE)) !=
+ BHSTR_FLAGS_FINAL)
+ log_errx(1, "received TextRequest PDU with invalid "
+ "flags: %u", bhstr->bhstr_flags);
+ if (ISCSI_SNLT(ntohl(bhstr->bhstr_cmdsn), conn->conn_cmdsn)) {
+ log_errx(1, "received TextRequest PDU with decreasing CmdSN: "
+ "was %u, is %u", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn));
+ }
+ conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn);
+ if ((bhstr->bhstr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0)
+ conn->conn_cmdsn++;
+
+ return (request);
+}
+
+/* Construct a new TextResponse PDU in reply to a request. */
+static struct pdu *
+text_new_response(struct pdu *request, uint32_t ttt, bool final)
+{
+ struct pdu *response;
+ struct connection *conn;
+ struct iscsi_bhs_text_request *bhstr;
+ struct iscsi_bhs_text_response *bhstr2;
+
+ bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
+ conn = request->pdu_connection;
+
+ response = pdu_new_response(request);
+ bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs;
+ bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE;
+ if (final)
+ bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL;
+ else
+ bhstr2->bhstr_flags = BHSTR_FLAGS_CONTINUE;
+ bhstr2->bhstr_lun = bhstr->bhstr_lun;
+ bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag;
+ bhstr2->bhstr_target_transfer_tag = ttt;
+ bhstr2->bhstr_statsn = htonl(conn->conn_statsn++);
+ bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn);
+ bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn);
+
+ return (response);
+}
+
+/* Receive a TextResponse PDU from a connection. */
+static struct pdu *
+text_receive_response(struct connection *conn)
+{
+ struct pdu *response;
+ struct iscsi_bhs_text_response *bhstr;
+ uint8_t flags;
+
+ response = pdu_new(conn);
+ pdu_receive(response);
+ if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE)
+ log_errx(1, "protocol error: received invalid opcode 0x%x",
+ response->pdu_bhs->bhs_opcode);
+ bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs;
+ flags = bhstr->bhstr_flags & (BHSTR_FLAGS_FINAL | BHSTR_FLAGS_CONTINUE);
+ switch (flags) {
+ case BHSTR_FLAGS_CONTINUE:
+ if (bhstr->bhstr_target_transfer_tag == 0xffffffff)
+ log_errx(1, "received continue TextResponse PDU with "
+ "invalid TTT 0x%x",
+ bhstr->bhstr_target_transfer_tag);
+ break;
+ case BHSTR_FLAGS_FINAL:
+ if (bhstr->bhstr_target_transfer_tag != 0xffffffff)
+ log_errx(1, "received final TextResponse PDU with "
+ "invalid TTT 0x%x",
+ bhstr->bhstr_target_transfer_tag);
+ break;
+ default:
+ log_errx(1, "received TextResponse PDU with invalid "
+ "flags: %u", bhstr->bhstr_flags);
+ }
+ if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) {
+ log_errx(1, "received TextResponse PDU with wrong StatSN: "
+ "is %u, should be %u", ntohl(bhstr->bhstr_statsn),
+ conn->conn_statsn + 1);
+ }
+ conn->conn_statsn = ntohl(bhstr->bhstr_statsn);
+
+ return (response);
+}
+
+/*
+ * Send a list of keys from the initiator to the target in a
+ * TextRequest PDU.
+ */
+void
+text_send_request(struct connection *conn, struct keys *request_keys)
+{
+ struct pdu *request;
+
+ request = text_new_request(conn, 0xffffffff);
+ keys_save_pdu(request_keys, request);
+ if (request->pdu_data_len == 0)
+ log_errx(1, "No keys to send in a TextRequest");
+ if (request->pdu_data_len >
+ (size_t)conn->conn_max_send_data_segment_length)
+ log_errx(1, "Keys to send in TextRequest are too long");
+
+ pdu_send(request);
+ pdu_delete(request);
+}
+
+/*
+ * Read a list of keys from the target in a series of TextResponse
+ * PDUs.
+ */
+struct keys *
+text_read_response(struct connection *conn)
+{
+ struct keys *response_keys;
+ char *keys_data;
+ size_t keys_len;
+ uint32_t ttt;
+
+ keys_data = NULL;
+ keys_len = 0;
+ ttt = 0xffffffff;
+ for (;;) {
+ struct pdu *request, *response;
+ struct iscsi_bhs_text_response *bhstr;
+
+ response = text_receive_response(conn);
+ bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs;
+ if (keys_data == NULL) {
+ ttt = bhstr->bhstr_target_transfer_tag;
+ keys_data = response->pdu_data;
+ keys_len = response->pdu_data_len;
+ response->pdu_data = NULL;
+ } else {
+ keys_data = realloc(keys_data,
+ keys_len + response->pdu_data_len);
+ if (keys_data == NULL)
+ log_err(1, "failed to grow keys block");
+ memcpy(keys_data + keys_len, response->pdu_data,
+ response->pdu_data_len);
+ keys_len += response->pdu_data_len;
+ }
+ if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) != 0) {
+ pdu_delete(response);
+ break;
+ }
+ if (bhstr->bhstr_target_transfer_tag != ttt)
+ log_errx(1, "received non-final TextRequest PDU with "
+ "invalid TTT 0x%x",
+ bhstr->bhstr_target_transfer_tag);
+ pdu_delete(response);
+
+ /* Send an empty request. */
+ request = text_new_request(conn, ttt);
+ pdu_send(request);
+ pdu_delete(request);
+ }
+
+ response_keys = keys_new();
+ keys_load(response_keys, keys_data, keys_len);
+ free(keys_data);
+ return (response_keys);
+}
+
+/*
+ * Read a list of keys from the initiator in a TextRequest PDU.
+ */
+struct keys *
+text_read_request(struct connection *conn, struct pdu **requestp)
+{
+ struct iscsi_bhs_text_request *bhstr;
+ struct pdu *request;
+ struct keys *request_keys;
+
+ request = text_receive_request(conn);
+ bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
+ if (bhstr->bhstr_target_transfer_tag != 0xffffffff)
+ log_errx(1, "received TextRequest PDU with invalid TTT 0x%x",
+ bhstr->bhstr_target_transfer_tag);
+ if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) {
+ log_errx(1, "received TextRequest PDU with wrong ExpStatSN: "
+ "is %u, should be %u", ntohl(bhstr->bhstr_expstatsn),
+ conn->conn_statsn);
+ }
+
+ request_keys = keys_new();
+ keys_load_pdu(request_keys, request);
+ *requestp = request;
+ return (request_keys);
+}
+
+/*
+ * Send a response back to the initiator as a series of TextResponse
+ * PDUs.
+ */
+void
+text_send_response(struct pdu *request, struct keys *response_keys)
+{
+ struct connection *conn = request->pdu_connection;
+ char *keys_data;
+ size_t keys_len;
+ size_t keys_offset;
+ uint32_t ttt;
+
+ keys_save(response_keys, &keys_data, &keys_len);
+ keys_offset = 0;
+ ttt = keys_len;
+ for (;;) {
+ struct pdu *request2, *response;
+ struct iscsi_bhs_text_request *bhstr;
+ size_t todo;
+ bool final;
+
+ todo = keys_len - keys_offset;
+ if (todo > (size_t)conn->conn_max_send_data_segment_length) {
+ final = false;
+ todo = conn->conn_max_send_data_segment_length;
+ } else {
+ final = true;
+ ttt = 0xffffffff;
+ }
+
+ response = text_new_response(request, ttt, final);
+ response->pdu_data = keys_data + keys_offset;
+ response->pdu_data_len = todo;
+ keys_offset += todo;
+
+ pdu_send(response);
+ response->pdu_data = NULL;
+ pdu_delete(response);
+
+ if (final)
+ break;
+
+ /*
+ * Wait for an empty request.
+ *
+ * XXX: Linux's Open-iSCSI initiator doesn't update
+ * ExpStatSN when receiving a TextResponse PDU.
+ */
+ request2 = text_receive_request(conn);
+ bhstr = (struct iscsi_bhs_text_request *)request2->pdu_bhs;
+ if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0)
+ log_errx(1, "received continuation TextRequest PDU "
+ "without F set");
+ if (pdu_data_segment_length(request2) != 0)
+ log_errx(1, "received non-empty continuation "
+ "TextRequest PDU");
+ if (bhstr->bhstr_target_transfer_tag != ttt)
+ log_errx(1, "received TextRequest PDU with invalid "
+ "TTT 0x%x", bhstr->bhstr_target_transfer_tag);
+ pdu_delete(request2);
+ }
+ free(keys_data);
+}
diff --git a/lib/libiscsiutil/utils.c b/lib/libiscsiutil/utils.c
new file mode 100644
index 000000000000..ef2d67106da5
--- /dev/null
+++ b/lib/libiscsiutil/utils.c
@@ -0,0 +1,115 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012 The FreeBSD Foundation
+ *
+ * This software was developed by Edward Tomasz Napierala under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include "libiscsiutil.h"
+
+#define MAX_NAME_LEN 223
+
+char *
+checked_strdup(const char *s)
+{
+ char *c;
+
+ c = strdup(s);
+ if (c == NULL)
+ log_err(1, "strdup");
+ return (c);
+}
+
+bool
+valid_iscsi_name(const char *name, void (*warn_fn)(const char *, ...))
+{
+ int i;
+
+ if (strlen(name) >= MAX_NAME_LEN) {
+ warn_fn("overlong name for target \"%s\"; max length allowed "
+ "by iSCSI specification is %d characters",
+ name, MAX_NAME_LEN);
+ return (false);
+ }
+
+ /*
+ * In the cases below, we don't return an error, just in case the admin
+ * was right, and we're wrong.
+ */
+ if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) {
+ for (i = strlen("iqn."); name[i] != '\0'; i++) {
+ /*
+ * XXX: We should verify UTF-8 normalisation, as defined
+ * by 3.2.6.2: iSCSI Name Encoding.
+ */
+ if (isalnum(name[i]))
+ continue;
+ if (name[i] == '-' || name[i] == '.' || name[i] == ':')
+ continue;
+ warn_fn("invalid character \"%c\" in target name "
+ "\"%s\"; allowed characters are letters, digits, "
+ "'-', '.', and ':'", name[i], name);
+ break;
+ }
+ /*
+ * XXX: Check more stuff: valid date and a valid reversed domain.
+ */
+ } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) {
+ if (strlen(name) != strlen("eui.") + 16)
+ warn_fn("invalid target name \"%s\"; the \"eui.\" "
+ "should be followed by exactly 16 hexadecimal "
+ "digits", name);
+ for (i = strlen("eui."); name[i] != '\0'; i++) {
+ if (!isxdigit(name[i])) {
+ warn_fn("invalid character \"%c\" in target "
+ "name \"%s\"; allowed characters are 1-9 "
+ "and A-F", name[i], name);
+ break;
+ }
+ }
+ } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) {
+ if (strlen(name) > strlen("naa.") + 32)
+ warn_fn("invalid target name \"%s\"; the \"naa.\" "
+ "should be followed by at most 32 hexadecimal "
+ "digits", name);
+ for (i = strlen("naa."); name[i] != '\0'; i++) {
+ if (!isxdigit(name[i])) {
+ warn_fn("invalid character \"%c\" in target "
+ "name \"%s\"; allowed characters are 1-9 "
+ "and A-F", name[i], name);
+ break;
+ }
+ }
+ } else {
+ warn_fn("invalid target name \"%s\"; should start with "
+ "either \"iqn.\", \"eui.\", or \"naa.\"",
+ name);
+ }
+ return (true);
+}