- /* do a forward lookup as well to prevent spoofing */
- hp = gethostbyname(name_buf);
- if (!hp) {
- strcpy(name_buf,def);
- rprintf(FERROR,"reverse name lookup failed\n");
- } else {
- for (p=hp->h_addr_list;*p;p++) {
- if (memcmp(*p, &sockin->sin_addr, hp->h_length) == 0) {
- break;
- }
- }
- if (!*p) {
- strcpy(name_buf,def);
- rprintf(FERROR,"reverse name lookup mismatch - spoofed address?\n");
- }
+
+/**
+ * Look up a name from @p ss into @p name_buf.
+ **/
+static int lookup_name(const struct sockaddr_storage *ss,
+ socklen_t ss_len,
+ char *name_buf, size_t name_buf_len,
+ char *port_buf, size_t port_buf_len)
+{
+ int name_err;
+
+#ifdef INET6
+ if (get_sockaddr_family(ss) == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
+ /* OK, so ss is in the IPv6 family, but it is really
+ * an IPv4 address: something like
+ * "::ffff:10.130.1.2". If we use it as-is, then the
+ * reverse lookup might fail or perhaps something else
+ * bad might happen. So instead we convert it to an
+ * equivalent address in the IPv4 address family. */
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in *sin;
+
+ memcpy(&sin6, ss, sizeof(sin6));
+ sin = (struct sockaddr_in *)ss;
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ ss_len = sizeof(struct sockaddr_in);
+#ifdef HAVE_SOCKADDR_LEN
+ sin->sin_len = ss_len;
+#endif
+ sin->sin_port = sin6.sin6_port;
+ /* FIXME: Isn't there a macro we can use here rather
+ * than grovelling through the struct? It might be
+ * wrong on some systems. */
+ memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],
+ sizeof(sin->sin_addr));
+ }
+#endif
+
+ /* reverse lookup */
+ name_err = getnameinfo((struct sockaddr *) ss, ss_len,
+ name_buf, name_buf_len,
+ port_buf, port_buf_len,
+ NI_NAMEREQD | NI_NUMERICSERV);
+ if (name_err != 0) {
+ strcpy(name_buf, default_name);
+ rprintf(FERROR, RSYNC_NAME ": name lookup failed: %s\n",
+ gai_strerror(name_err));
+ return name_err;