|
@@ -11,6 +11,7 @@
|
|
|
#include <linux/string.h>
|
|
|
#include <linux/bio.h>
|
|
|
#include <linux/blkdev.h>
|
|
|
+#include <linux/dns_resolver.h>
|
|
|
#include <net/tcp.h>
|
|
|
|
|
|
#include <linux/ceph/libceph.h>
|
|
@@ -1077,6 +1078,101 @@ static void addr_set_port(struct sockaddr_storage *ss, int p)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Unlike other *_pton function semantics, zero indicates success.
|
|
|
+ */
|
|
|
+static int ceph_pton(const char *str, size_t len, struct sockaddr_storage *ss,
|
|
|
+ char delim, const char **ipend)
|
|
|
+{
|
|
|
+ struct sockaddr_in *in4 = (void *)ss;
|
|
|
+ struct sockaddr_in6 *in6 = (void *)ss;
|
|
|
+
|
|
|
+ memset(ss, 0, sizeof(*ss));
|
|
|
+
|
|
|
+ if (in4_pton(str, len, (u8 *)&in4->sin_addr.s_addr, delim, ipend)) {
|
|
|
+ ss->ss_family = AF_INET;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (in6_pton(str, len, (u8 *)&in6->sin6_addr.s6_addr, delim, ipend)) {
|
|
|
+ ss->ss_family = AF_INET6;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Extract hostname string and resolve using kernel DNS facility.
|
|
|
+ */
|
|
|
+#ifdef CONFIG_CEPH_LIB_USE_DNS_RESOLVER
|
|
|
+static int ceph_dns_resolve_name(const char *name, size_t namelen,
|
|
|
+ struct sockaddr_storage *ss, char delim, const char **ipend)
|
|
|
+{
|
|
|
+ const char *end, *delim_p;
|
|
|
+ char *colon_p, *ip_addr = NULL;
|
|
|
+ int ip_len, ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The end of the hostname occurs immediately preceding the delimiter or
|
|
|
+ * the port marker (':') where the delimiter takes precedence.
|
|
|
+ */
|
|
|
+ delim_p = memchr(name, delim, namelen);
|
|
|
+ colon_p = memchr(name, ':', namelen);
|
|
|
+
|
|
|
+ if (delim_p && colon_p)
|
|
|
+ end = delim_p < colon_p ? delim_p : colon_p;
|
|
|
+ else if (!delim_p && colon_p)
|
|
|
+ end = colon_p;
|
|
|
+ else {
|
|
|
+ end = delim_p;
|
|
|
+ if (!end) /* case: hostname:/ */
|
|
|
+ end = name + namelen;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (end <= name)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* do dns_resolve upcall */
|
|
|
+ ip_len = dns_query(NULL, name, end - name, NULL, &ip_addr, NULL);
|
|
|
+ if (ip_len > 0)
|
|
|
+ ret = ceph_pton(ip_addr, ip_len, ss, -1, NULL);
|
|
|
+ else
|
|
|
+ ret = -ESRCH;
|
|
|
+
|
|
|
+ kfree(ip_addr);
|
|
|
+
|
|
|
+ *ipend = end;
|
|
|
+
|
|
|
+ pr_info("resolve '%.*s' (ret=%d): %s\n", (int)(end - name), name,
|
|
|
+ ret, ret ? "failed" : ceph_pr_addr(ss));
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+#else
|
|
|
+static inline int ceph_dns_resolve_name(const char *name, size_t namelen,
|
|
|
+ struct sockaddr_storage *ss, char delim, const char **ipend)
|
|
|
+{
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+/*
|
|
|
+ * Parse a server name (IP or hostname). If a valid IP address is not found
|
|
|
+ * then try to extract a hostname to resolve using userspace DNS upcall.
|
|
|
+ */
|
|
|
+static int ceph_parse_server_name(const char *name, size_t namelen,
|
|
|
+ struct sockaddr_storage *ss, char delim, const char **ipend)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = ceph_pton(name, namelen, ss, delim, ipend);
|
|
|
+ if (ret)
|
|
|
+ ret = ceph_dns_resolve_name(name, namelen, ss, delim, ipend);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Parse an ip[:port] list into an addr array. Use the default
|
|
|
* monitor port if a port isn't specified.
|
|
@@ -1085,15 +1181,13 @@ int ceph_parse_ips(const char *c, const char *end,
|
|
|
struct ceph_entity_addr *addr,
|
|
|
int max_count, int *count)
|
|
|
{
|
|
|
- int i;
|
|
|
+ int i, ret = -EINVAL;
|
|
|
const char *p = c;
|
|
|
|
|
|
dout("parse_ips on '%.*s'\n", (int)(end-c), c);
|
|
|
for (i = 0; i < max_count; i++) {
|
|
|
const char *ipend;
|
|
|
struct sockaddr_storage *ss = &addr[i].in_addr;
|
|
|
- struct sockaddr_in *in4 = (void *)ss;
|
|
|
- struct sockaddr_in6 *in6 = (void *)ss;
|
|
|
int port;
|
|
|
char delim = ',';
|
|
|
|
|
@@ -1102,15 +1196,11 @@ int ceph_parse_ips(const char *c, const char *end,
|
|
|
p++;
|
|
|
}
|
|
|
|
|
|
- memset(ss, 0, sizeof(*ss));
|
|
|
- if (in4_pton(p, end - p, (u8 *)&in4->sin_addr.s_addr,
|
|
|
- delim, &ipend))
|
|
|
- ss->ss_family = AF_INET;
|
|
|
- else if (in6_pton(p, end - p, (u8 *)&in6->sin6_addr.s6_addr,
|
|
|
- delim, &ipend))
|
|
|
- ss->ss_family = AF_INET6;
|
|
|
- else
|
|
|
+ ret = ceph_parse_server_name(p, end - p, ss, delim, &ipend);
|
|
|
+ if (ret)
|
|
|
goto bad;
|
|
|
+ ret = -EINVAL;
|
|
|
+
|
|
|
p = ipend;
|
|
|
|
|
|
if (delim == ']') {
|
|
@@ -1155,7 +1245,7 @@ int ceph_parse_ips(const char *c, const char *end,
|
|
|
|
|
|
bad:
|
|
|
pr_err("parse_ips bad ip '%.*s'\n", (int)(end - c), c);
|
|
|
- return -EINVAL;
|
|
|
+ return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL(ceph_parse_ips);
|
|
|
|