Browse code

ifconfig-ipv6(-push): allow using hostnames

Similarly to ifconfig(-push), its IPv6 counterpart is now able to
accept hostnames as well instead of IP addresses in numeric form.

Basically this means that the user is now allowed to specify
something like this:

ifconfig-ipv6-push my.hostname.cx/64

This is exactly the same behaviour that we already have with
ifconfig(-push).

The generic code introduced in this patch will be later used to
implement the /bits parsing support for IPv4 addresses.

Trac: #808
Signed-off-by: Antonio Quartulli <a@unstable.cc>
Acked-by: Selva Nair <selva.nair@gmail.com>
Message-Id: <20171203041426.25316-1-a@unstable.cc>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg15969.html
Signed-off-by: David Sommerseth <davids@openvpn.net>

Antonio Quartulli authored on 2017/12/03 13:14:26
Showing 4 changed files
... ...
@@ -1023,67 +1023,6 @@ get_ip_addr(const char *ip_string, int msglevel, bool *error)
1023 1023
     return ret;
1024 1024
 }
1025 1025
 
1026
-/* helper: parse a text string containing an IPv6 address + netbits
1027
- * in "standard format" (2001:dba::/32)
1028
- * "/nn" is optional, default to /64 if missing
1029
- *
1030
- * return true if parsing succeeded, modify *network and *netbits
1031
- */
1032
-bool
1033
-get_ipv6_addr( const char *prefix_str, struct in6_addr *network,
1034
-               unsigned int *netbits, int msglevel)
1035
-{
1036
-    char *sep, *endp;
1037
-    int bits;
1038
-    struct in6_addr t_network;
1039
-
1040
-    sep = strchr( prefix_str, '/' );
1041
-    if (sep == NULL)
1042
-    {
1043
-        bits = 64;
1044
-    }
1045
-    else
1046
-    {
1047
-        bits = strtol( sep+1, &endp, 10 );
1048
-        if (*endp != '\0' || bits < 0 || bits > 128)
1049
-        {
1050
-            msg(msglevel, "IPv6 prefix '%s': invalid '/bits' spec", prefix_str);
1051
-            return false;
1052
-        }
1053
-    }
1054
-
1055
-    /* temporary replace '/' in caller-provided string with '\0', otherwise
1056
-     * inet_pton() will refuse prefix string
1057
-     * (alternative would be to strncpy() the prefix to temporary buffer)
1058
-     */
1059
-
1060
-    if (sep != NULL)
1061
-    {
1062
-        *sep = '\0';
1063
-    }
1064
-
1065
-    if (inet_pton( AF_INET6, prefix_str, &t_network ) != 1)
1066
-    {
1067
-        msg(msglevel, "IPv6 prefix '%s': invalid IPv6 address", prefix_str);
1068
-        return false;
1069
-    }
1070
-
1071
-    if (sep != NULL)
1072
-    {
1073
-        *sep = '/';
1074
-    }
1075
-
1076
-    if (netbits != NULL)
1077
-    {
1078
-        *netbits = bits;
1079
-    }
1080
-    if (network != NULL)
1081
-    {
1082
-        *network = t_network;
1083
-    }
1084
-    return true;                /* parsing OK, values set */
1085
-}
1086
-
1087 1026
 /**
1088 1027
  * Returns newly allocated string containing address part without "/nn".
1089 1028
  *
... ...
@@ -819,8 +819,4 @@ void options_string_import(struct options *options,
819 819
                            unsigned int *option_types_found,
820 820
                            struct env_set *es);
821 821
 
822
-bool get_ipv6_addr( const char *prefix_str, struct in6_addr *network,
823
-                    unsigned int *netbits, int msglevel );
824
-
825
-
826 822
 #endif /* ifndef OPTIONS_H */
... ...
@@ -75,12 +75,116 @@ sf2gaf(const unsigned int getaddr_flags,
75 75
 /*
76 76
  * Functions related to the translation of DNS names to IP addresses.
77 77
  */
78
+static int
79
+get_addr_generic(sa_family_t af, unsigned int flags, const char *hostname,
80
+                 void *network, unsigned int *netbits,
81
+                 int resolve_retry_seconds, volatile int *signal_received,
82
+                 int msglevel)
83
+{
84
+    char *endp, *sep, *var_host = NULL;
85
+    struct addrinfo *ai = NULL;
86
+    unsigned long bits;
87
+    uint8_t max_bits;
88
+    int ret = -1;
89
+
90
+    if (!hostname)
91
+    {
92
+        msg(M_NONFATAL, "Can't resolve null hostname!");
93
+        goto out;
94
+    }
95
+
96
+    /* assign family specific default values */
97
+    switch (af)
98
+    {
99
+        case AF_INET:
100
+            bits = 0;
101
+            max_bits = sizeof(in_addr_t) * 8;
102
+            break;
103
+        case AF_INET6:
104
+            bits = 64;
105
+            max_bits = sizeof(struct in6_addr) * 8;
106
+            break;
107
+        default:
108
+            msg(M_WARN,
109
+                "Unsupported AF family passed to getaddrinfo for %s (%d)",
110
+                hostname, af);
111
+            goto out;
112
+    }
113
+
114
+    /* we need to modify the hostname received as input, but we don't want to
115
+     * touch it directly as it might be a constant string.
116
+     *
117
+     * Therefore, we clone the string here and free it at the end of the
118
+     * function */
119
+    var_host = strdup(hostname);
120
+    if (!var_host)
121
+    {
122
+        msg(M_NONFATAL | M_ERRNO,
123
+            "Can't allocate hostname buffer for getaddrinfo");
124
+        goto out;
125
+    }
126
+
127
+    /* check if this hostname has a /bits suffix */
128
+    sep = strchr(var_host , '/');
129
+    if (sep)
130
+    {
131
+        bits = strtoul(sep + 1, &endp, 10);
132
+        if ((*endp != '\0') || (bits > max_bits))
133
+        {
134
+            msg(msglevel, "IP prefix '%s': invalid '/bits' spec (%s)", hostname,
135
+                sep + 1);
136
+            goto out;
137
+        }
138
+        *sep = '\0';
139
+    }
140
+
141
+    ret = openvpn_getaddrinfo(flags & ~GETADDR_HOST_ORDER, var_host, NULL,
142
+                              resolve_retry_seconds, signal_received, af, &ai);
143
+    if ((ret == 0) && network)
144
+    {
145
+        struct in6_addr *ip6;
146
+        in_addr_t *ip4;
147
+
148
+        switch (af)
149
+        {
150
+            case AF_INET:
151
+                ip4 = network;
152
+                *ip4 = ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
153
+
154
+                if (flags & GETADDR_HOST_ORDER)
155
+                {
156
+                    *ip4 = ntohl(*ip4);
157
+                }
158
+                break;
159
+            case AF_INET6:
160
+                ip6 = network;
161
+                *ip6 = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
162
+                break;
163
+            default:
164
+                /* can't get here because 'af' was previously checked */
165
+                msg(M_WARN,
166
+                    "Unsupported AF family for %s (%d)", var_host, af);
167
+                goto out;
168
+        }
169
+    }
170
+
171
+    if (netbits)
172
+    {
173
+        *netbits = bits;
174
+    }
175
+
176
+    /* restore '/' separator, if any */
177
+    if (sep)
178
+    {
179
+        *sep = '/';
180
+    }
181
+out:
182
+    freeaddrinfo(ai);
183
+    free(var_host);
184
+
185
+    return ret;
186
+}
78 187
 
79
-/*
80
- * Translate IP addr or hostname to in_addr_t.
81
- * If resolve error, try again for
82
- * resolve_retry_seconds seconds.
83
- */
84 188
 in_addr_t
85 189
 getaddr(unsigned int flags,
86 190
         const char *hostname,
... ...
@@ -88,20 +192,19 @@ getaddr(unsigned int flags,
88 88
         bool *succeeded,
89 89
         volatile int *signal_received)
90 90
 {
91
-    struct addrinfo *ai;
91
+    in_addr_t addr;
92 92
     int status;
93
-    status = openvpn_getaddrinfo(flags & ~GETADDR_HOST_ORDER, hostname, NULL,
94
-                                 resolve_retry_seconds, signal_received, AF_INET, &ai);
93
+
94
+    status = get_addr_generic(AF_INET, flags, hostname, &addr, NULL,
95
+                              resolve_retry_seconds, signal_received,
96
+                              M_WARN);
95 97
     if (status==0)
96 98
     {
97
-        struct in_addr ia;
98 99
         if (succeeded)
99 100
         {
100 101
             *succeeded = true;
101 102
         }
102
-        ia = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
103
-        freeaddrinfo(ai);
104
-        return (flags & GETADDR_HOST_ORDER) ? ntohl(ia.s_addr) : ia.s_addr;
103
+        return addr;
105 104
     }
106 105
     else
107 106
     {
... ...
@@ -113,6 +216,19 @@ getaddr(unsigned int flags,
113 113
     }
114 114
 }
115 115
 
116
+bool
117
+get_ipv6_addr(const char *hostname, struct in6_addr *network,
118
+              unsigned int *netbits, int msglevel)
119
+{
120
+    if (get_addr_generic(AF_INET6, GETADDR_RESOLVE, hostname, network, netbits,
121
+                         0, NULL, msglevel) < 0)
122
+    {
123
+        return false;
124
+    }
125
+
126
+    return true;                /* parsing OK, values set */
127
+}
128
+
116 129
 static inline bool
117 130
 streqnull(const char *a, const char *b)
118 131
 {
... ...
@@ -532,12 +532,24 @@ bool unix_socket_get_peer_uid_gid(const socket_descriptor_t sd, int *uid, int *g
532 532
 
533 533
 #define GETADDR_CACHE_MASK              (GETADDR_DATAGRAM|GETADDR_PASSIVE)
534 534
 
535
+/**
536
+ * Translate an IPv4 addr or hostname from string form to in_addr_t
537
+ *
538
+ * In case of resolve error, it will try again for
539
+ * resolve_retry_seconds seconds.
540
+ */
535 541
 in_addr_t getaddr(unsigned int flags,
536 542
                   const char *hostname,
537 543
                   int resolve_retry_seconds,
538 544
                   bool *succeeded,
539 545
                   volatile int *signal_received);
540 546
 
547
+/**
548
+ * Translate an IPv6 addr or hostname from string form to in6_addr
549
+ */
550
+bool get_ipv6_addr(const char *hostname, struct in6_addr *network,
551
+                   unsigned int *netbits, int msglevel);
552
+
541 553
 int openvpn_getaddrinfo(unsigned int flags,
542 554
                         const char *hostname,
543 555
                         const char *servname,