summaryrefslogtreecommitdiff
path: root/src/openvpn/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/socket.c')
-rw-r--r--src/openvpn/socket.c140
1 files changed, 128 insertions, 12 deletions
diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c
index 211e744..c76d206 100644
--- a/src/openvpn/socket.c
+++ b/src/openvpn/socket.c
@@ -74,12 +74,116 @@ sf2gaf(const unsigned int getaddr_flags,
/*
* Functions related to the translation of DNS names to IP addresses.
*/
+static int
+get_addr_generic(sa_family_t af, unsigned int flags, const char *hostname,
+ void *network, unsigned int *netbits,
+ int resolve_retry_seconds, volatile int *signal_received,
+ int msglevel)
+{
+ char *endp, *sep, *var_host = NULL;
+ struct addrinfo *ai = NULL;
+ unsigned long bits;
+ uint8_t max_bits;
+ int ret = -1;
+
+ if (!hostname)
+ {
+ msg(M_NONFATAL, "Can't resolve null hostname!");
+ goto out;
+ }
+
+ /* assign family specific default values */
+ switch (af)
+ {
+ case AF_INET:
+ bits = 0;
+ max_bits = sizeof(in_addr_t) * 8;
+ break;
+ case AF_INET6:
+ bits = 64;
+ max_bits = sizeof(struct in6_addr) * 8;
+ break;
+ default:
+ msg(M_WARN,
+ "Unsupported AF family passed to getaddrinfo for %s (%d)",
+ hostname, af);
+ goto out;
+ }
+
+ /* we need to modify the hostname received as input, but we don't want to
+ * touch it directly as it might be a constant string.
+ *
+ * Therefore, we clone the string here and free it at the end of the
+ * function */
+ var_host = strdup(hostname);
+ if (!var_host)
+ {
+ msg(M_NONFATAL | M_ERRNO,
+ "Can't allocate hostname buffer for getaddrinfo");
+ goto out;
+ }
+
+ /* check if this hostname has a /bits suffix */
+ sep = strchr(var_host , '/');
+ if (sep)
+ {
+ bits = strtoul(sep + 1, &endp, 10);
+ if ((*endp != '\0') || (bits > max_bits))
+ {
+ msg(msglevel, "IP prefix '%s': invalid '/bits' spec (%s)", hostname,
+ sep + 1);
+ goto out;
+ }
+ *sep = '\0';
+ }
+
+ ret = openvpn_getaddrinfo(flags & ~GETADDR_HOST_ORDER, var_host, NULL,
+ resolve_retry_seconds, signal_received, af, &ai);
+ if ((ret == 0) && network)
+ {
+ struct in6_addr *ip6;
+ in_addr_t *ip4;
+
+ switch (af)
+ {
+ case AF_INET:
+ ip4 = network;
+ *ip4 = ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
+
+ if (flags & GETADDR_HOST_ORDER)
+ {
+ *ip4 = ntohl(*ip4);
+ }
+ break;
+ case AF_INET6:
+ ip6 = network;
+ *ip6 = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
+ break;
+ default:
+ /* can't get here because 'af' was previously checked */
+ msg(M_WARN,
+ "Unsupported AF family for %s (%d)", var_host, af);
+ goto out;
+ }
+ }
+
+ if (netbits)
+ {
+ *netbits = bits;
+ }
+
+ /* restore '/' separator, if any */
+ if (sep)
+ {
+ *sep = '/';
+ }
+out:
+ freeaddrinfo(ai);
+ free(var_host);
+
+ return ret;
+}
-/*
- * Translate IP addr or hostname to in_addr_t.
- * If resolve error, try again for
- * resolve_retry_seconds seconds.
- */
in_addr_t
getaddr(unsigned int flags,
const char *hostname,
@@ -87,20 +191,19 @@ getaddr(unsigned int flags,
bool *succeeded,
volatile int *signal_received)
{
- struct addrinfo *ai;
+ in_addr_t addr;
int status;
- status = openvpn_getaddrinfo(flags & ~GETADDR_HOST_ORDER, hostname, NULL,
- resolve_retry_seconds, signal_received, AF_INET, &ai);
+
+ status = get_addr_generic(AF_INET, flags, hostname, &addr, NULL,
+ resolve_retry_seconds, signal_received,
+ M_WARN);
if (status==0)
{
- struct in_addr ia;
if (succeeded)
{
*succeeded = true;
}
- ia = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
- freeaddrinfo(ai);
- return (flags & GETADDR_HOST_ORDER) ? ntohl(ia.s_addr) : ia.s_addr;
+ return addr;
}
else
{
@@ -112,6 +215,19 @@ getaddr(unsigned int flags,
}
}
+bool
+get_ipv6_addr(const char *hostname, struct in6_addr *network,
+ unsigned int *netbits, int msglevel)
+{
+ if (get_addr_generic(AF_INET6, GETADDR_RESOLVE, hostname, network, netbits,
+ 0, NULL, msglevel) < 0)
+ {
+ return false;
+ }
+
+ return true; /* parsing OK, values set */
+}
+
static inline bool
streqnull(const char *a, const char *b)
{