mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-09-23 22:44:58 +02:00
742cd15649
That way, string_is_valid_ipv6_address() can benefit from it
274 lines
7.9 KiB
C
274 lines
7.9 KiB
C
/* Copyright (c) 2003-2004, Roger Dingledine
|
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
|
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
|
/* See LICENSE for licensing information */
|
|
|
|
/**
|
|
* \file inaddr.c
|
|
* \brief Convert in_addr and in6_addr to and from strings.
|
|
**/
|
|
|
|
#include "lib/net/inaddr.h"
|
|
|
|
#include "lib/cc/torint.h"
|
|
#include "lib/log/util_bug.h"
|
|
#include "lib/net/inaddr_st.h"
|
|
#include "lib/string/compat_ctype.h"
|
|
#include "lib/string/compat_string.h"
|
|
#include "lib/string/printf.h"
|
|
#include "lib/string/scanf.h"
|
|
#include "lib/string/util_string.h"
|
|
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <winsock2.h>
|
|
#endif
|
|
|
|
/** Set *addr to the IP address (in dotted-quad notation) stored in *str.
|
|
* Return 1 on success, 0 if *str is badly formatted.
|
|
* (Like inet_aton(str,addr), but works on Windows and Solaris.)
|
|
*/
|
|
int
|
|
tor_inet_aton(const char *str, struct in_addr* addr)
|
|
{
|
|
unsigned a,b,c,d;
|
|
char more;
|
|
if (tor_sscanf(str, "%3u.%3u.%3u.%3u%c", &a,&b,&c,&d,&more) != 4)
|
|
return 0;
|
|
if (a > 255) return 0;
|
|
if (b > 255) return 0;
|
|
if (c > 255) return 0;
|
|
if (d > 255) return 0;
|
|
addr->s_addr = htonl((a<<24) | (b<<16) | (c<<8) | d);
|
|
return 1;
|
|
}
|
|
|
|
/** Given an IPv4 in_addr struct *<b>in</b> (in network order, as usual),
|
|
* write it as a string into the <b>buf_len</b>-byte buffer in
|
|
* <b>buf</b>. Returns a non-negative integer on success.
|
|
* Returns -1 on failure.
|
|
*/
|
|
int
|
|
tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len)
|
|
{
|
|
uint32_t a = ntohl(in->s_addr);
|
|
return tor_snprintf(buf, buf_len, "%d.%d.%d.%d",
|
|
(int)(uint8_t)((a>>24)&0xff),
|
|
(int)(uint8_t)((a>>16)&0xff),
|
|
(int)(uint8_t)((a>>8 )&0xff),
|
|
(int)(uint8_t)((a )&0xff));
|
|
}
|
|
|
|
/** Given <b>af</b>==AF_INET and <b>src</b> a struct in_addr, or
|
|
* <b>af</b>==AF_INET6 and <b>src</b> a struct in6_addr, try to format the
|
|
* address and store it in the <b>len</b>-byte buffer <b>dst</b>. Returns
|
|
* <b>dst</b> on success, NULL on failure.
|
|
*
|
|
* (Like inet_ntop(af,src,dst,len), but works on platforms that don't have it:
|
|
* Tor sometimes needs to format ipv6 addresses even on platforms without ipv6
|
|
* support.) */
|
|
const char *
|
|
tor_inet_ntop(int af, const void *src, char *dst, size_t len)
|
|
{
|
|
if (af == AF_INET) {
|
|
if (tor_inet_ntoa(src, dst, len) < 0)
|
|
return NULL;
|
|
else
|
|
return dst;
|
|
} else if (af == AF_INET6) {
|
|
const struct in6_addr *addr = src;
|
|
char buf[64], *cp;
|
|
int longestGapLen = 0, longestGapPos = -1, i,
|
|
curGapPos = -1, curGapLen = 0;
|
|
uint16_t words[8];
|
|
for (i = 0; i < 8; ++i) {
|
|
words[i] = (((uint16_t)addr->s6_addr[2*i])<<8) + addr->s6_addr[2*i+1];
|
|
}
|
|
if (words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 &&
|
|
words[4] == 0 && ((words[5] == 0 && words[6] && words[7]) ||
|
|
(words[5] == 0xffff))) {
|
|
/* This is an IPv4 address. */
|
|
if (words[5] == 0) {
|
|
tor_snprintf(buf, sizeof(buf), "::%d.%d.%d.%d",
|
|
addr->s6_addr[12], addr->s6_addr[13],
|
|
addr->s6_addr[14], addr->s6_addr[15]);
|
|
} else {
|
|
tor_snprintf(buf, sizeof(buf), "::%x:%d.%d.%d.%d", words[5],
|
|
addr->s6_addr[12], addr->s6_addr[13],
|
|
addr->s6_addr[14], addr->s6_addr[15]);
|
|
}
|
|
if ((strlen(buf) + 1) > len) /* +1 for \0 */
|
|
return NULL;
|
|
strlcpy(dst, buf, len);
|
|
return dst;
|
|
}
|
|
i = 0;
|
|
while (i < 8) {
|
|
if (words[i] == 0) {
|
|
curGapPos = i++;
|
|
curGapLen = 1;
|
|
while (i<8 && words[i] == 0) {
|
|
++i; ++curGapLen;
|
|
}
|
|
if (curGapLen > longestGapLen) {
|
|
longestGapPos = curGapPos;
|
|
longestGapLen = curGapLen;
|
|
}
|
|
} else {
|
|
++i;
|
|
}
|
|
}
|
|
if (longestGapLen<=1)
|
|
longestGapPos = -1;
|
|
|
|
cp = buf;
|
|
for (i = 0; i < 8; ++i) {
|
|
if (words[i] == 0 && longestGapPos == i) {
|
|
if (i == 0)
|
|
*cp++ = ':';
|
|
*cp++ = ':';
|
|
while (i < 8 && words[i] == 0)
|
|
++i;
|
|
--i; /* to compensate for loop increment. */
|
|
} else {
|
|
tor_snprintf(cp, sizeof(buf)-(cp-buf), "%x", (unsigned)words[i]);
|
|
cp += strlen(cp);
|
|
if (i != 7)
|
|
*cp++ = ':';
|
|
}
|
|
}
|
|
*cp = '\0';
|
|
if ((strlen(buf) + 1) > len) /* +1 for \0 */
|
|
return NULL;
|
|
strlcpy(dst, buf, len);
|
|
return dst;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/** Given <b>af</b>==AF_INET or <b>af</b>==AF_INET6, and a string <b>src</b>
|
|
* encoding an IPv4 address or IPv6 address correspondingly, try to parse the
|
|
* address and store the result in <b>dst</b> (which must have space for a
|
|
* struct in_addr or a struct in6_addr, as appropriate). Return 1 on success,
|
|
* 0 on a bad parse, and -1 on a bad <b>af</b>.
|
|
*
|
|
* (Like inet_pton(af,src,dst) but works on platforms that don't have it: Tor
|
|
* sometimes needs to format ipv6 addresses even on platforms without ipv6
|
|
* support.) */
|
|
int
|
|
tor_inet_pton(int af, const char *src, void *dst)
|
|
{
|
|
if (af == AF_INET) {
|
|
return tor_inet_aton(src, dst);
|
|
} else if (af == AF_INET6) {
|
|
ssize_t len = strlen(src);
|
|
|
|
/* Reject if src has needless trailing ':'. */
|
|
if (len > 2 && src[len - 1] == ':' && src[len - 2] != ':') {
|
|
return 0;
|
|
}
|
|
|
|
struct in6_addr *out = dst;
|
|
uint16_t words[8];
|
|
int gapPos = -1, i, setWords=0;
|
|
const char *dot = strchr(src, '.');
|
|
const char *eow; /* end of words. */
|
|
memset(words, 0xf8, sizeof(words));
|
|
if (dot == src)
|
|
return 0;
|
|
else if (!dot)
|
|
eow = src+strlen(src);
|
|
else {
|
|
unsigned byte1,byte2,byte3,byte4;
|
|
char more;
|
|
for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow)
|
|
;
|
|
if (*eow != ':')
|
|
return 0;
|
|
++eow;
|
|
|
|
/* We use "scanf" because some platform inet_aton()s are too lax
|
|
* about IPv4 addresses of the form "1.2.3" */
|
|
if (tor_sscanf(eow, "%3u.%3u.%3u.%3u%c",
|
|
&byte1,&byte2,&byte3,&byte4,&more) != 4)
|
|
return 0;
|
|
|
|
if (byte1 > 255 || byte2 > 255 || byte3 > 255 || byte4 > 255)
|
|
return 0;
|
|
|
|
words[6] = (byte1<<8) | byte2;
|
|
words[7] = (byte3<<8) | byte4;
|
|
setWords += 2;
|
|
}
|
|
|
|
i = 0;
|
|
while (src < eow) {
|
|
if (i > 7)
|
|
return 0;
|
|
if (TOR_ISXDIGIT(*src)) {
|
|
char *next;
|
|
long r = strtol(src, &next, 16);
|
|
if (next == NULL || next == src) {
|
|
/* The 'next == src' error case can happen on versions of openbsd
|
|
* which treat "0xfoo" as an error, rather than as "0" followed by
|
|
* "xfoo". */
|
|
return 0;
|
|
}
|
|
|
|
len = *next == '\0' ? eow - src : next - src;
|
|
if (len > 4)
|
|
return 0;
|
|
if (len > 1 && !TOR_ISXDIGIT(src[1]))
|
|
return 0; /* 0x is not valid */
|
|
|
|
tor_assert(r >= 0);
|
|
tor_assert(r < 65536);
|
|
words[i++] = (uint16_t)r;
|
|
setWords++;
|
|
src = next;
|
|
if (*src != ':' && src != eow)
|
|
return 0;
|
|
++src;
|
|
} else if (*src == ':' && i > 0 && gapPos == -1) {
|
|
gapPos = i;
|
|
++src;
|
|
} else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' &&
|
|
gapPos == -1) {
|
|
gapPos = i;
|
|
src += 2;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (setWords > 8 ||
|
|
(setWords == 8 && gapPos != -1) ||
|
|
(setWords < 8 && gapPos == -1))
|
|
return 0;
|
|
|
|
if (gapPos >= 0) {
|
|
int nToMove = setWords - (dot ? 2 : 0) - gapPos;
|
|
int gapLen = 8 - setWords;
|
|
tor_assert(nToMove >= 0);
|
|
memmove(&words[gapPos+gapLen], &words[gapPos],
|
|
sizeof(uint16_t)*nToMove);
|
|
memset(&words[gapPos], 0, sizeof(uint16_t)*gapLen);
|
|
}
|
|
for (i = 0; i < 8; ++i) {
|
|
out->s6_addr[2*i ] = words[i] >> 8;
|
|
out->s6_addr[2*i+1] = words[i] & 0xff;
|
|
}
|
|
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|