Patchwork [1/2] Make liburiparser optional

login
register
mail settings
Submitter Chuck Lever
Date Aug. 27, 2013, 7:27 p.m.
Message ID <20130827192718.7597.58037.stgit@manray.1015granger.net>
Download mbox | patch
Permalink /patch/270207/
State Accepted
Headers show

Comments

Chuck Lever - Aug. 27, 2013, 7:27 p.m.
liburiparser is the correct and standard way to construct and parse
RFC 3986-complaint URIs.  FedFS represents NFS locations as NFS URIs
when storing locations in an NSDB.

Unfortunately not every Linux distribution packages liburiparser.
The liburiparser build requirement puts fedfs-utils 0.9 and later
offlimits for such distributions.

As a stop-gap, provide hand-coded URI parsers which can fill in if
liburiparser is not available.

To use the new internal parsers instead of liburiparser, specify
"--without-liburiparser" on the ./configure command line.  The
default setting is "--with-liburiparser".

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 configure.ac                |   10 ++
 src/include/nsdb.h          |   11 +++
 src/libnsdb/administrator.c |   95 +++++++++++++++++++++++
 src/libnsdb/fileserver.c    |  175 +++++++++++++++++++++++++++++++++++++++++++
 src/libnsdb/path.c          |    5 +
 5 files changed, 292 insertions(+), 4 deletions(-)

Patch

diff --git a/configure.ac b/configure.ac
index 679dda3..570fcc1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,6 +54,12 @@  AC_ARG_WITH([statedir],
 	AC_SUBST(statedir)
 	AC_DEFINE_UNQUOTED([FEDFS_DEFAULT_STATEDIR], ["$statedir"],
 		[Define to the default pathname of the directory where fedfsd maintains persistent state.])
+AC_ARG_WITH([liburiparser],
+	[AS_HELP_STRING([--with-liburiparser],
+		[Disable if liburiparser is not available @<:@default=enabled@:>@])],
+	with_uriparser=$withval,
+	with_uriparser=yes)
+	AC_SUBST(with_uriparser)
 
 # Publication date stamp for man pages
 pubdate=`date +"%e %B %Y"`
@@ -124,11 +130,13 @@  AC_CHECK_LIB([xml2], [xmlParseFile],
 		 AC_DEFINE([HAVE_LIBXML2], [1],
 			   [Define if you have libxml2])],
 		[AC_MSG_ERROR([libxml2 not found.])])
-AC_CHECK_LIB([uriparser], [uriParseUriA],
+if test "$with_uriparser" = yes; then
+  AC_CHECK_LIB([uriparser], [uriParseUriA],
 		[AC_SUBST([LIBURIPARSER], ["-luriparser"])
 		 AC_DEFINE([HAVE_LIBURIPARSER], [1],
 			   [Define if you have liburiparser])],
 		[AC_MSG_ERROR([liburiparser not found.])])
+fi
 AC_CHECK_LIB([crypto], [X509_LOOKUP_file],
 		[AC_SUBST([LIBCRYPTO], ["-lcrypto"])
 		 AC_DEFINE([HAVE_LIBCRYPTO], [1],
diff --git a/src/include/nsdb.h b/src/include/nsdb.h
index 4f6aadc..224b671 100644
--- a/src/include/nsdb.h
+++ b/src/include/nsdb.h
@@ -26,9 +26,16 @@ 
 #ifndef _FEDFS_NSDB_H_
 #define _FEDFS_NSDB_H_
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <netdb.h>
 #include <ldap.h>
+
+#ifdef HAVE_LIBURIPARSER
 #include <uriparser/Uri.h>
+#endif
 
 #include "fedfs_admin.h"
 #include "fedfs.h"
@@ -452,12 +459,16 @@  FedFsStatus	 nsdb_path_array_to_fedfspathname(char * const *path_array,
 				FedFsPathName *fpath);
 FedFsStatus	 nsdb_fedfspathname_to_path_array(FedFsPathName fpath,
 				char ***path_array);
+
+#ifdef HAVE_LIBURIPARSER
 void		 nsdb_assign_textrange(UriTextRangeA *text,
 				const char *string);
 FedFsStatus	 nsdb_path_array_to_uri_pathname(char * const *path_array,
 				UriUriA *uri);
 FedFsStatus	 nsdb_uri_pathname_to_path_array(const UriUriA *uri,
 				char ***path_array);
+#endif	/* HAVE_LIBURIPARSER */
+
 
 /**
  ** x.509 certificate utilities
diff --git a/src/libnsdb/administrator.c b/src/libnsdb/administrator.c
index 4fd93ec..81a2537 100644
--- a/src/libnsdb/administrator.c
+++ b/src/libnsdb/administrator.c
@@ -612,6 +612,8 @@  nsdb_construct_fsl_dn(const char *nce, const char *fsn_uuid, const char *fsl_uui
 	return dn;
 }
 
+#ifdef HAVE_LIBURIPARSER
+
 /**
  * Build a UriUriA for the location information in "nfsfsl"
  *
@@ -691,6 +693,99 @@  out:
 	return retval;
 }
 
+#else	/* !HAVE_LIBURIPARSER */
+
+/**
+ * Check if a hostname is an IPv6 presentation address
+ *
+ * @param hostname a NUL-terminated C string containing a hostname
+ * @return boolean
+ */
+static _Bool
+nsdb_hostname_is_ipv6_addr(const char *hostname)
+{
+	struct addrinfo hints = {
+		.ai_flags	= AI_NUMERICHOST,
+		.ai_family	= AF_INET6,
+	};
+	struct addrinfo *ai;
+	int err;
+
+	err = getaddrinfo(hostname, NULL, &hints, &ai);
+	if (err)
+		return false;
+	freeaddrinfo(ai);
+	return true;
+}
+
+/**
+ * Construct an NFS URI for this location
+ *
+ * @param nfsfsl an initialized struct fedfs_nfs_fsl
+ * @param nfsuri OUT: a NUL-terminated C string containing an NFS URI
+ * @return a FedFsStatus code
+ *
+ * Caller must free "nfsuri" with free(3).
+ */
+static FedFsStatus
+nsdb_construct_nfsuri(const struct fedfs_nfs_fsl *nfsfsl, char **nfsuri)
+{
+	char *result, *pathname;
+	FedFsStatus retval;
+	size_t len;
+
+	/*
+	 * We're cheating here: in most cases, the POSIX pathname
+	 * is appropriate to use for the URI.  For cases that are
+	 * broken here, use liburiparser.
+	 */
+	retval = nsdb_path_array_to_posix(nfsfsl->fn_nfspath, &pathname);
+	if (retval != FEDFS_OK)
+		goto out;
+	retval = FEDFS_ERR_SVRFAULT;
+
+	len = strlen("nfs://") + 2 + strlen(nfsfsl->fn_fslhost) + 8 +
+		strlen(pathname) + 1;
+	result = calloc(len, sizeof(char));
+	if (result == NULL)
+		goto out;
+	result[0] = '\0';
+
+	/*
+	 * URI scheme
+	 */
+	strcat(result, "nfs://");
+
+	/*
+	 * URI authority
+	 */
+	if (nsdb_hostname_is_ipv6_addr(nfsfsl->fn_fslhost)) {
+		strcat(result, "[");
+		strcat(result, nfsfsl->fn_fslhost);
+		strcat(result, "]");
+	} else
+		strcat(result, nfsfsl->fn_fslhost);
+	if (nfsfsl->fn_fslport != NFS_PORT && nfsfsl->fn_fslport != 0) {
+		char portbuf[8];
+		sprintf(portbuf, ":%u", nfsfsl->fn_fslport);
+		strcat(result, portbuf);
+	}
+
+	/*
+	 * URI path
+	 */
+	strcat(result, pathname);
+
+	*nfsuri = result;
+	retval = FEDFS_OK;
+
+out:
+	free(pathname);
+	return retval;
+}
+
+#endif	/* !HAVE_LIBURIPARSER */
+
 static const char *nsdb_ldap_true	= "TRUE";
 static const char *nsdb_ldap_false	= "FALSE";
 
diff --git a/src/libnsdb/fileserver.c b/src/libnsdb/fileserver.c
index d3f7560..96fad2c 100644
--- a/src/libnsdb/fileserver.c
+++ b/src/libnsdb/fileserver.c
@@ -39,8 +39,6 @@ 
 #include <unistd.h>
 #include <netdb.h>
 
-#include <uriparser/Uri.h>
-
 #include "nsdb.h"
 #include "nsdb-internal.h"
 #include "xlog.h"
@@ -712,6 +710,8 @@  nsdb_parse_annotations(struct berval **values, char ***annotations)
 	return FEDFS_OK;
 }
 
+#ifdef HAVE_LIBURIPARSER
+
 /**
  * Unmarshal a parsed NFS URI object into an NFS FSL
  *
@@ -834,6 +834,177 @@  out:
 	return retval;
 }
 
+#else	/* !HAVE_URIPARSER */
+
+/**
+ * Check if a hostname is an IPv6 presentation address
+ *
+ * @param hostname a NUL-terminated C string containing a hostname
+ * @return boolean
+ */
+static _Bool
+nsdb_hostname_is_ipv6_addr(const char *hostname)
+{
+	struct addrinfo hints = {
+		.ai_flags	= AI_NUMERICHOST,
+		.ai_family	= AF_INET6,
+	};
+	struct addrinfo *ai;
+	int err;
+
+	err = getaddrinfo(hostname, NULL, &hints, &ai);
+	if (err)
+		return false;
+	freeaddrinfo(ai);
+	return true;
+}
+
+/**
+ * Parse authority portion of a URI that contains an IPv6 address
+ *
+ * @param authority a NUL-terminated C string containing authority portion of URI
+ * @param nfsl OUT: fedfs_nfs_fsl structure to fill in
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+nsdb_parse_authority_with_ipv6(char *authority, struct fedfs_nfs_fsl *nfsl)
+{
+	char *pos, *hostname;
+	unsigned short port;
+
+	hostname = authority + 1;
+	pos = strchr(hostname, ']');
+	if (pos == NULL) {
+		xlog(L_ERROR, "%s: NFS URI contains unbalanced square brackets",
+			__func__);
+		return FEDFS_ERR_NSDB_RESPONSE;
+	}
+	*pos = '\0';
+	if (!nsdb_hostname_is_ipv6_addr(hostname)) {
+		xlog(L_ERROR, "%s: NFS URI contains non-IPv6 address in square brackets",
+			__func__);
+		return FEDFS_ERR_NSDB_RESPONSE;
+	}
+
+	pos++;
+	port = 0;
+	if (*pos == ':') {
+		pos++;
+		if (!nsdb_parse_port_string(pos, &port)) {
+			xlog(L_ERROR, "%s: NFS URI has invalid port",
+				__func__, pos);
+			return FEDFS_ERR_NSDB_RESPONSE;
+		}
+	}
+
+	strcpy(nfsl->fn_fslhost, hostname);
+	nfsl->fn_fslport = port;
+	return FEDFS_OK;
+}
+
+/**
+ * Parse authority portion of a URI that does not contain an IPv6 address
+ *
+ * @param authority a NUL-terminated C string containing authority portion of URI
+ * @param nfsl OUT: fedfs_nfs_fsl structure to fill in
+ * @return a FedFsStatus code
+ */
+static int
+nsdb_parse_authority_without_ipv6(char *authority, struct fedfs_nfs_fsl *nfsl)
+{
+	char *pos, *hostname;
+	unsigned short port;
+
+	port = 0;
+	hostname = authority;
+	pos = strchr(hostname, ':');
+	if (pos != NULL) {
+		*pos++ = '\0';
+		if (!nsdb_parse_port_string(pos, &port)) {
+			xlog(L_ERROR, "%s: NFS URI has invalid port",
+				__func__, pos);
+			return FEDFS_ERR_NSDB_RESPONSE;
+		}
+	}
+
+	strcpy(nfsl->fn_fslhost, hostname);
+	nfsl->fn_fslport = port;
+	return FEDFS_OK;
+}
+
+/**
+ * Parse an NFS URI into an NFS FSL
+ *
+ * @param uri a NUL-terminated C string containing URI to parse
+ * @param nfsl OUT: fedfs_nfs_fsl structure to fill in
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+nsdb_parse_nfs_uri_freehand(const char *uri, struct fedfs_nfs_fsl *nfsl)
+{
+	static char authbuf[BUFSIZ];
+	char *pos = (char *)uri;
+	char *pathname, **path;
+	FedFsStatus retval;
+
+	if (strncasecmp(pos, "nfs://", 6) != 0) {
+		xlog(L_ERROR, "%s: non-NFS URI", __func__);
+		return FEDFS_ERR_NSDB_RESPONSE;
+	}
+	pos += 6;
+
+	pathname = strchr(pos, '/');
+	if (pathname == NULL) {
+		xlog(L_ERROR, "%s: NFS URI contains no pathname", __func__);
+		return FEDFS_ERR_NSDB_RESPONSE;
+	}
+
+	retval = nsdb_posix_to_path_array(pathname, &path);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	authbuf[0] = '\0';
+	strncpy(authbuf, pos, pathname - pos);
+	if (authbuf[0] == '[')
+		retval = nsdb_parse_authority_with_ipv6(authbuf, nfsl);
+	else
+		retval = nsdb_parse_authority_without_ipv6(authbuf, nfsl);
+	if (retval != FEDFS_OK) {
+		nsdb_free_string_array(path);
+		return retval;
+	}
+	nfsl->fn_nfspath = path;
+	return FEDFS_OK;
+}
+
+/**
+ * Parse an NFS URI into a hostname and pathname
+ *
+ * @param attr NUL-terminated C string containing LDAP attribute name
+ * @param values URI string value returned from LDAP server
+ * @param nfsl OUT: fedfs_nfs_fsl structure to fill in
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+nsdb_parse_nfs_uri(const char *attr, struct berval **values,
+		struct fedfs_nfs_fsl *nfsl)
+{
+	if (values[0] == NULL) {
+		xlog(L_ERROR, "%s: NULL value for attribute %s",
+			__func__, attr);
+		return FEDFS_ERR_NSDB_RESPONSE;
+	}
+	if (values[1] != NULL) {
+		xlog(L_ERROR, "%s: Expecting only one value for attribute %s",
+			__func__, attr);
+		return FEDFS_ERR_NSDB_RESPONSE;
+	}
+
+	return nsdb_parse_nfs_uri_freehand((char *)values[0]->bv_val, nfsl);
+}
+
+#endif	/* !HAVE_URIPARSER */
+
 /**
  * Parse the values of each attribute in a fedfsFsl object
  *
diff --git a/src/libnsdb/path.c b/src/libnsdb/path.c
index 11bf73b..c14d516 100644
--- a/src/libnsdb/path.c
+++ b/src/libnsdb/path.c
@@ -38,7 +38,6 @@ 
 #include <ldap.h>
 
 #include <netinet/in.h>
-#include <uriparser/Uri.h>
 
 #include "nsdb.h"
 #include "junction.h"
@@ -589,6 +588,8 @@  nsdb_fedfspathname_to_path_array(FedFsPathName fpath, char ***path_array)
 	return FEDFS_OK;
 }
 
+#ifdef HAVE_LIBURIPARSER
+
 /**
  * Assign the value of "string" to a UriTextRangeA field
  *
@@ -816,3 +817,5 @@  nsdb_uri_pathname_to_path_array(const UriUriA *uri, char ***path_array)
 	*path_array = result;
 	return FEDFS_OK;
 }
+
+#endif	/* HAVE_LIBURIPARSER */