Bump copyright date to 2019
[tor.git] / src / lib / net / inaddr.c
blobd9ae7cd562eeb1d338f5a122a34bd32bf18ddf53
1 /* Copyright (c) 2003-2004, Roger Dingledine
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2019, 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/log/util_bug.h"
15 #include "lib/net/inaddr_st.h"
16 #include "lib/string/compat_ctype.h"
17 #include "lib/string/compat_string.h"
18 #include "lib/string/printf.h"
19 #include "lib/string/scanf.h"
20 #include "lib/string/util_string.h"
22 #ifdef HAVE_ARPA_INET_H
23 #include <arpa/inet.h>
24 #endif
26 #include <stdlib.h>
27 #include <string.h>
29 #ifdef _WIN32
30 #include <winsock2.h>
31 #endif
33 /** Set *addr to the IP address (in dotted-quad notation) stored in *str.
34 * Return 1 on success, 0 if *str is badly formatted.
35 * (Like inet_aton(str,addr), but works on Windows and Solaris.)
37 int
38 tor_inet_aton(const char *str, struct in_addr* addr)
40 unsigned a,b,c,d;
41 char more;
42 if (tor_sscanf(str, "%3u.%3u.%3u.%3u%c", &a,&b,&c,&d,&more) != 4)
43 return 0;
44 if (a > 255) return 0;
45 if (b > 255) return 0;
46 if (c > 255) return 0;
47 if (d > 255) return 0;
48 addr->s_addr = htonl((a<<24) | (b<<16) | (c<<8) | d);
49 return 1;
52 /** Given an IPv4 in_addr struct *<b>in</b> (in network order, as usual),
53 * write it as a string into the <b>buf_len</b>-byte buffer in
54 * <b>buf</b>. Returns a non-negative integer on success.
55 * Returns -1 on failure.
57 int
58 tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len)
60 uint32_t a = ntohl(in->s_addr);
61 return tor_snprintf(buf, buf_len, "%d.%d.%d.%d",
62 (int)(uint8_t)((a>>24)&0xff),
63 (int)(uint8_t)((a>>16)&0xff),
64 (int)(uint8_t)((a>>8 )&0xff),
65 (int)(uint8_t)((a )&0xff));
68 /** Given <b>af</b>==AF_INET and <b>src</b> a struct in_addr, or
69 * <b>af</b>==AF_INET6 and <b>src</b> a struct in6_addr, try to format the
70 * address and store it in the <b>len</b>-byte buffer <b>dst</b>. Returns
71 * <b>dst</b> on success, NULL on failure.
73 * (Like inet_ntop(af,src,dst,len), but works on platforms that don't have it:
74 * Tor sometimes needs to format ipv6 addresses even on platforms without ipv6
75 * support.) */
76 const char *
77 tor_inet_ntop(int af, const void *src, char *dst, size_t len)
79 if (af == AF_INET) {
80 if (tor_inet_ntoa(src, dst, len) < 0)
81 return NULL;
82 else
83 return dst;
84 } else if (af == AF_INET6) {
85 const struct in6_addr *addr = src;
86 char buf[64], *cp;
87 int longestGapLen = 0, longestGapPos = -1, i,
88 curGapPos = -1, curGapLen = 0;
89 uint16_t words[8];
90 for (i = 0; i < 8; ++i) {
91 words[i] = (((uint16_t)addr->s6_addr[2*i])<<8) + addr->s6_addr[2*i+1];
93 if (words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 &&
94 words[4] == 0 && ((words[5] == 0 && words[6] && words[7]) ||
95 (words[5] == 0xffff))) {
96 /* This is an IPv4 address. */
97 if (words[5] == 0) {
98 tor_snprintf(buf, sizeof(buf), "::%d.%d.%d.%d",
99 addr->s6_addr[12], addr->s6_addr[13],
100 addr->s6_addr[14], addr->s6_addr[15]);
101 } else {
102 tor_snprintf(buf, sizeof(buf), "::%x:%d.%d.%d.%d", words[5],
103 addr->s6_addr[12], addr->s6_addr[13],
104 addr->s6_addr[14], addr->s6_addr[15]);
106 if ((strlen(buf) + 1) > len) /* +1 for \0 */
107 return NULL;
108 strlcpy(dst, buf, len);
109 return dst;
111 i = 0;
112 while (i < 8) {
113 if (words[i] == 0) {
114 curGapPos = i++;
115 curGapLen = 1;
116 while (i<8 && words[i] == 0) {
117 ++i; ++curGapLen;
119 if (curGapLen > longestGapLen) {
120 longestGapPos = curGapPos;
121 longestGapLen = curGapLen;
123 } else {
124 ++i;
127 if (longestGapLen<=1)
128 longestGapPos = -1;
130 cp = buf;
131 for (i = 0; i < 8; ++i) {
132 if (words[i] == 0 && longestGapPos == i) {
133 if (i == 0)
134 *cp++ = ':';
135 *cp++ = ':';
136 while (i < 8 && words[i] == 0)
137 ++i;
138 --i; /* to compensate for loop increment. */
139 } else {
140 tor_snprintf(cp, sizeof(buf)-(cp-buf), "%x", (unsigned)words[i]);
141 cp += strlen(cp);
142 if (i != 7)
143 *cp++ = ':';
146 *cp = '\0';
147 if ((strlen(buf) + 1) > len) /* +1 for \0 */
148 return NULL;
149 strlcpy(dst, buf, len);
150 return dst;
151 } else {
152 return NULL;
156 /** Given <b>af</b>==AF_INET or <b>af</b>==AF_INET6, and a string <b>src</b>
157 * encoding an IPv4 address or IPv6 address correspondingly, try to parse the
158 * address and store the result in <b>dst</b> (which must have space for a
159 * struct in_addr or a struct in6_addr, as appropriate). Return 1 on success,
160 * 0 on a bad parse, and -1 on a bad <b>af</b>.
162 * (Like inet_pton(af,src,dst) but works on platforms that don't have it: Tor
163 * sometimes needs to format ipv6 addresses even on platforms without ipv6
164 * support.) */
166 tor_inet_pton(int af, const char *src, void *dst)
168 if (af == AF_INET) {
169 return tor_inet_aton(src, dst);
170 } else if (af == AF_INET6) {
171 ssize_t len = strlen(src);
173 /* Reject if src has needless trailing ':'. */
174 if (len > 2 && src[len - 1] == ':' && src[len - 2] != ':') {
175 return 0;
178 struct in6_addr *out = dst;
179 uint16_t words[8];
180 int gapPos = -1, i, setWords=0;
181 const char *dot = strchr(src, '.');
182 const char *eow; /* end of words. */
183 memset(words, 0xf8, sizeof(words));
184 if (dot == src)
185 return 0;
186 else if (!dot)
187 eow = src+strlen(src);
188 else {
189 unsigned byte1,byte2,byte3,byte4;
190 char more;
191 for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow)
193 if (*eow != ':')
194 return 0;
195 ++eow;
197 /* We use "scanf" because some platform inet_aton()s are too lax
198 * about IPv4 addresses of the form "1.2.3" */
199 if (tor_sscanf(eow, "%3u.%3u.%3u.%3u%c",
200 &byte1,&byte2,&byte3,&byte4,&more) != 4)
201 return 0;
203 if (byte1 > 255 || byte2 > 255 || byte3 > 255 || byte4 > 255)
204 return 0;
206 words[6] = (byte1<<8) | byte2;
207 words[7] = (byte3<<8) | byte4;
208 setWords += 2;
211 i = 0;
212 while (src < eow) {
213 if (i > 7)
214 return 0;
215 if (TOR_ISXDIGIT(*src)) {
216 char *next;
217 long r = strtol(src, &next, 16);
218 if (next == NULL || next == src) {
219 /* The 'next == src' error case can happen on versions of openbsd
220 * which treat "0xfoo" as an error, rather than as "0" followed by
221 * "xfoo". */
222 return 0;
225 len = *next == '\0' ? eow - src : next - src;
226 if (len > 4)
227 return 0;
228 if (len > 1 && !TOR_ISXDIGIT(src[1]))
229 return 0; /* 0x is not valid */
231 tor_assert(r >= 0);
232 tor_assert(r < 65536);
233 words[i++] = (uint16_t)r;
234 setWords++;
235 src = next;
236 if (*src != ':' && src != eow)
237 return 0;
238 ++src;
239 } else if (*src == ':' && i > 0 && gapPos == -1) {
240 gapPos = i;
241 ++src;
242 } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' &&
243 gapPos == -1) {
244 gapPos = i;
245 src += 2;
246 } else {
247 return 0;
251 if (setWords > 8 ||
252 (setWords == 8 && gapPos != -1) ||
253 (setWords < 8 && gapPos == -1))
254 return 0;
256 if (gapPos >= 0) {
257 int nToMove = setWords - (dot ? 2 : 0) - gapPos;
258 int gapLen = 8 - setWords;
259 tor_assert(nToMove >= 0);
260 memmove(&words[gapPos+gapLen], &words[gapPos],
261 sizeof(uint16_t)*nToMove);
262 memset(&words[gapPos], 0, sizeof(uint16_t)*gapLen);
264 for (i = 0; i < 8; ++i) {
265 out->s6_addr[2*i ] = words[i] >> 8;
266 out->s6_addr[2*i+1] = words[i] & 0xff;
269 return 1;
270 } else {
271 return -1;