Update copyrights to 2021, using "make update-copyright"
[tor.git] / src / lib / net / inaddr.c
blob0ec39870065417f591ae2587afdfb13e3a26c366
1 /* Copyright (c) 2003-2004, Roger Dingledine
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
6 /**
7 * \file inaddr.c
8 * \brief Convert in_addr and in6_addr to and from strings.
9 **/
11 #include "lib/net/inaddr.h"
13 #include "lib/cc/torint.h"
14 #include "lib/container/smartlist.h"
15 #include "lib/log/util_bug.h"
16 #include "lib/malloc/malloc.h"
17 #include "lib/net/inaddr_st.h"
18 #include "lib/string/compat_ctype.h"
19 #include "lib/string/compat_string.h"
20 #include "lib/string/printf.h"
21 #include "lib/string/scanf.h"
22 #include "lib/string/util_string.h"
24 #ifdef HAVE_ARPA_INET_H
25 #include <arpa/inet.h>
26 #endif
28 #include <stdlib.h>
29 #include <string.h>
31 #ifdef _WIN32
32 #include <winsock2.h>
33 #endif
35 /** Set *addr to the IP address (in dotted-quad notation) stored in *str.
36 * Return 1 on success, 0 if *str is badly formatted.
37 * (Like inet_aton(str,addr), but works on Windows and Solaris.)
39 int
40 tor_inet_aton(const char *str, struct in_addr *addr)
42 unsigned a, b, c, d;
43 char more;
44 bool is_octal = false;
45 smartlist_t *sl = NULL;
47 if (tor_sscanf(str, "%3u.%3u.%3u.%3u%c", &a, &b, &c, &d, &more) != 4)
48 return 0;
50 /* Parse the octets and check them for leading zeros. */
51 sl = smartlist_new();
52 smartlist_split_string(sl, str, ".", 0, 0);
53 SMARTLIST_FOREACH(sl, const char *, octet, {
54 is_octal = (strlen(octet) > 1 && octet[0] == '0');
55 if (is_octal) {
56 break;
58 });
59 SMARTLIST_FOREACH(sl, char *, octet, tor_free(octet));
60 smartlist_free(sl);
62 if (is_octal)
63 return 0;
65 if (a > 255) return 0;
66 if (b > 255) return 0;
67 if (c > 255) return 0;
68 if (d > 255) return 0;
69 addr->s_addr = htonl((a<<24) | (b<<16) | (c<<8) | d);
70 return 1;
73 /** Given an IPv4 in_addr struct *<b>in</b> (in network order, as usual),
74 * write it as a string into the <b>buf_len</b>-byte buffer in
75 * <b>buf</b>. Returns a non-negative integer on success.
76 * Returns -1 on failure.
78 int
79 tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len)
81 uint32_t a = ntohl(in->s_addr);
82 return tor_snprintf(buf, buf_len, "%d.%d.%d.%d",
83 (int)(uint8_t)((a>>24)&0xff),
84 (int)(uint8_t)((a>>16)&0xff),
85 (int)(uint8_t)((a>>8 )&0xff),
86 (int)(uint8_t)((a )&0xff));
89 /** Given <b>af</b>==AF_INET and <b>src</b> a struct in_addr, or
90 * <b>af</b>==AF_INET6 and <b>src</b> a struct in6_addr, try to format the
91 * address and store it in the <b>len</b>-byte buffer <b>dst</b>. Returns
92 * <b>dst</b> on success, NULL on failure.
94 * (Like inet_ntop(af,src,dst,len), but works on platforms that don't have it:
95 * Tor sometimes needs to format ipv6 addresses even on platforms without ipv6
96 * support.) */
97 const char *
98 tor_inet_ntop(int af, const void *src, char *dst, size_t len)
100 if (af == AF_INET) {
101 if (tor_inet_ntoa(src, dst, len) < 0)
102 return NULL;
103 else
104 return dst;
105 } else if (af == AF_INET6) {
106 const struct in6_addr *addr = src;
107 char buf[64], *cp;
108 int longestGapLen = 0, longestGapPos = -1, i,
109 curGapPos = -1, curGapLen = 0;
110 uint16_t words[8];
111 for (i = 0; i < 8; ++i) {
112 words[i] = (((uint16_t)addr->s6_addr[2*i])<<8) + addr->s6_addr[2*i+1];
114 if (words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 &&
115 words[4] == 0 && ((words[5] == 0 && words[6] && words[7]) ||
116 (words[5] == 0xffff))) {
117 /* This is an IPv4 address. */
118 if (words[5] == 0) {
119 tor_snprintf(buf, sizeof(buf), "::%d.%d.%d.%d",
120 addr->s6_addr[12], addr->s6_addr[13],
121 addr->s6_addr[14], addr->s6_addr[15]);
122 } else {
123 tor_snprintf(buf, sizeof(buf), "::%x:%d.%d.%d.%d", words[5],
124 addr->s6_addr[12], addr->s6_addr[13],
125 addr->s6_addr[14], addr->s6_addr[15]);
127 if ((strlen(buf) + 1) > len) /* +1 for \0 */
128 return NULL;
129 strlcpy(dst, buf, len);
130 return dst;
132 i = 0;
133 while (i < 8) {
134 if (words[i] == 0) {
135 curGapPos = i++;
136 curGapLen = 1;
137 while (i<8 && words[i] == 0) {
138 ++i; ++curGapLen;
140 if (curGapLen > longestGapLen) {
141 longestGapPos = curGapPos;
142 longestGapLen = curGapLen;
144 } else {
145 ++i;
148 if (longestGapLen<=1)
149 longestGapPos = -1;
151 cp = buf;
152 for (i = 0; i < 8; ++i) {
153 if (words[i] == 0 && longestGapPos == i) {
154 if (i == 0)
155 *cp++ = ':';
156 *cp++ = ':';
157 while (i < 8 && words[i] == 0)
158 ++i;
159 --i; /* to compensate for loop increment. */
160 } else {
161 tor_snprintf(cp, sizeof(buf)-(cp-buf), "%x", (unsigned)words[i]);
162 cp += strlen(cp);
163 if (i != 7)
164 *cp++ = ':';
167 *cp = '\0';
168 if ((strlen(buf) + 1) > len) /* +1 for \0 */
169 return NULL;
170 strlcpy(dst, buf, len);
171 return dst;
172 } else {
173 return NULL;
177 /** Given <b>af</b>==AF_INET or <b>af</b>==AF_INET6, and a string <b>src</b>
178 * encoding an IPv4 address or IPv6 address correspondingly, try to parse the
179 * address and store the result in <b>dst</b> (which must have space for a
180 * struct in_addr or a struct in6_addr, as appropriate). Return 1 on success,
181 * 0 on a bad parse, and -1 on a bad <b>af</b>.
183 * (Like inet_pton(af,src,dst) but works on platforms that don't have it: Tor
184 * sometimes needs to format ipv6 addresses even on platforms without ipv6
185 * support.) */
187 tor_inet_pton(int af, const char *src, void *dst)
189 if (af == AF_INET) {
190 return tor_inet_aton(src, dst);
191 } else if (af == AF_INET6) {
192 ssize_t len = strlen(src);
194 /* Reject if src has needless trailing ':'. */
195 if (len > 2 && src[len - 1] == ':' && src[len - 2] != ':') {
196 return 0;
199 struct in6_addr *out = dst;
200 uint16_t words[8];
201 int gapPos = -1, i, setWords=0;
202 const char *dot = strchr(src, '.');
203 const char *eow; /* end of words. */
204 memset(words, 0xf8, sizeof(words));
205 if (dot == src)
206 return 0;
207 else if (!dot)
208 eow = src+strlen(src);
209 else {
210 unsigned byte1,byte2,byte3,byte4;
211 char more;
212 for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow)
214 if (*eow != ':')
215 return 0;
216 ++eow;
218 /* We use "scanf" because some platform inet_aton()s are too lax
219 * about IPv4 addresses of the form "1.2.3" */
220 if (tor_sscanf(eow, "%3u.%3u.%3u.%3u%c",
221 &byte1,&byte2,&byte3,&byte4,&more) != 4)
222 return 0;
224 if (byte1 > 255 || byte2 > 255 || byte3 > 255 || byte4 > 255)
225 return 0;
227 words[6] = (byte1<<8) | byte2;
228 words[7] = (byte3<<8) | byte4;
229 setWords += 2;
232 i = 0;
233 while (src < eow) {
234 if (i > 7)
235 return 0;
236 if (TOR_ISXDIGIT(*src)) {
237 char *next;
238 long r = strtol(src, &next, 16);
239 if (next == NULL || next == src) {
240 /* The 'next == src' error case can happen on versions of openbsd
241 * which treat "0xfoo" as an error, rather than as "0" followed by
242 * "xfoo". */
243 return 0;
246 len = *next == '\0' ? eow - src : next - src;
247 if (len > 4)
248 return 0;
249 if (len > 1 && !TOR_ISXDIGIT(src[1]))
250 return 0; /* 0x is not valid */
252 tor_assert(r >= 0);
253 tor_assert(r < 65536);
254 words[i++] = (uint16_t)r;
255 setWords++;
256 src = next;
257 if (*src != ':' && src != eow)
258 return 0;
259 ++src;
260 } else if (*src == ':' && i > 0 && gapPos == -1) {
261 gapPos = i;
262 ++src;
263 } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' &&
264 gapPos == -1) {
265 gapPos = i;
266 src += 2;
267 } else {
268 return 0;
272 if (setWords > 8 ||
273 (setWords == 8 && gapPos != -1) ||
274 (setWords < 8 && gapPos == -1))
275 return 0;
277 if (gapPos >= 0) {
278 int nToMove = setWords - (dot ? 2 : 0) - gapPos;
279 int gapLen = 8 - setWords;
280 tor_assert(nToMove >= 0);
281 memmove(&words[gapPos+gapLen], &words[gapPos],
282 sizeof(uint16_t)*nToMove);
283 memset(&words[gapPos], 0, sizeof(uint16_t)*gapLen);
285 for (i = 0; i < 8; ++i) {
286 out->s6_addr[2*i ] = words[i] >> 8;
287 out->s6_addr[2*i+1] = words[i] & 0xff;
290 return 1;
291 } else {
292 return -1;