*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation; only version 2 of the License is
+ * applicable.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
struct pingobj
{
- double timeout;
- int ttl;
- int addrfamily;
- char *data;
+ double timeout;
+ int ttl;
+ int addrfamily;
+ char *data;
+
+ struct sockaddr_storage *srcaddr;
+ socklen_t srcaddrlen;
- char errmsg[PING_ERRMSG_LEN];
+ char errmsg[PING_ERRMSG_LEN];
- pinghost_t *head;
+ pinghost_t *head;
};
/*
if (obj->data != NULL)
free (obj->data);
+ if (obj->srcaddr != NULL)
+ free (obj->srcaddr);
+
free (obj);
return;
obj->addrfamily = PING_DEF_AF;
ret = -1;
}
+ if (obj->srcaddr != NULL)
+ {
+ free (obj->srcaddr);
+ obj->srcaddr = NULL;
+ }
break;
case PING_OPT_DATA:
obj->data = strdup ((const char *) value);
break;
+ case PING_OPT_SOURCE:
+ {
+ char *hostname = (char *) value;
+ struct addrinfo ai_hints;
+ struct addrinfo *ai_list;
+ int status;
+#if WITH_DEBUG
+ if (obj->addrfamily != AF_UNSPEC)
+ {
+ dprintf ("Resetting obj->addrfamily to AF_UNSPEC.\n");
+ }
+#endif
+ memset ((void *) &ai_hints, '\0', sizeof (ai_hints));
+ ai_hints.ai_family = obj->addrfamily = AF_UNSPEC;
+#if defined(AI_ADDRCONFIG)
+ ai_hints.ai_flags = AI_ADDRCONFIG;
+#endif
+ status = getaddrinfo (hostname, NULL, &ai_hints, &ai_list);
+ if (status != 0)
+ {
+ ping_set_error (obj, "getaddrinfo",
+ status == EAI_SYSTEM
+ ? strerror (errno)
+ : gai_strerror (status));
+ ret = -1;
+ break;
+ }
+#if WITH_DEBUG
+ if (ai_list->ai_next != NULL)
+ {
+ dprintf ("hostname = `%s' is ambiguous.\n", hostname);
+ }
+#endif
+ if (obj->srcaddr == NULL)
+ {
+ obj->srcaddrlen = 0;
+ obj->srcaddr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage));
+ if (obj->srcaddr == NULL)
+ {
+ ping_set_error (obj, "malloc",
+ strerror (errno));
+ ret = -1;
+ freeaddrinfo (ai_list);
+ break;
+ }
+ }
+ memset ((void *) obj->srcaddr, '\0', sizeof (struct sockaddr_storage));
+ assert (ai_list->ai_addrlen <= sizeof (struct sockaddr_storage));
+ memcpy ((void *) obj->srcaddr, (const void *) ai_list->ai_addr,
+ ai_list->ai_addrlen);
+ obj->srcaddrlen = ai_list->ai_addrlen;
+ obj->addrfamily = ai_list->ai_family;
+
+ freeaddrinfo (ai_list);
+ } /* case PING_OPT_SOURCE */
+ break;
+
default:
ret = -2;
} /* switch (option) */
#ifdef AI_ADDRCONFIG
ai_hints.ai_flags |= AI_ADDRCONFIG;
#endif
+#ifdef AI_CANONNAME
+ ai_hints.ai_flags |= AI_CANONNAME;
+#endif
ai_hints.ai_family = obj->addrfamily;
ai_hints.ai_socktype = SOCK_RAW;
continue;
}
-/*
- * The majority vote of operating systems has decided that you don't need to
- * bind here. This code should be reactivated to bind to a specific address,
- * though. See the `-I' option of `ping(1)' (GNU). -octo
- */
-#if 0
- if (bind (ph->fd, (struct sockaddr *) &sockaddr, sockaddr_len) == -1)
+ if (obj->srcaddr != NULL)
{
- dprintf ("bind: %s\n", strerror (errno));
- ping_set_error (obj, "bind", strerror (errno));
- close (ph->fd);
- ph->fd = -1;
- continue;
+ assert (obj->srcaddrlen > 0);
+ assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
+
+ if (bind (ph->fd, (struct sockaddr *) obj->srcaddr, obj->srcaddrlen) == -1)
+ {
+ dprintf ("bind: %s\n", strerror (errno));
+ ping_set_error (obj, "bind", strerror (errno));
+ close (ph->fd);
+ ph->fd = -1;
+ continue;
+ }
}
-#endif
assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
memset (ph->addr, '\0', sizeof (struct sockaddr_storage));
ph->addrlen = ai_ptr->ai_addrlen;
ph->addrfamily = ai_ptr->ai_family;
+#ifdef AI_CANONNAME
+ if ((ai_ptr->ai_canonname != NULL)
+ && (strcmp (ph->hostname, ai_ptr->ai_canonname) != 0))
+ {
+ char *old_hostname;
+
+ dprintf ("ph->hostname = %s; ai_ptr->ai_canonname = %s;\n",
+ ph->hostname, ai_ptr->ai_canonname);
+
+ old_hostname = ph->hostname;
+ if ((ph->hostname = strdup (ai_ptr->ai_canonname)) == NULL)
+ {
+ /* strdup failed, falling back to old hostname */
+ ph->hostname = old_hostname;
+ }
+ else if (old_hostname != NULL)
+ {
+ free (old_hostname);
+ }
+ }
+#endif /* AI_CANONNAME */
+
break;
}
while (cur != NULL)
{
- if (strcasecmp (host, cur->hostname))
+ if (strcasecmp (host, cur->hostname) == 0)
break;
pre = cur;