/*
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Carbonite Inc., 756 N Pastoria Ave
* Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
*
* Author: Dustin J. Mitchell <dustin@zmanda.com>
*/
/*
* Utility routines for handling sockaddrs
*/
#include "amanda.h"
#include "sockaddr-util.h"
void
dump_sockaddr(
sockaddr_union *sa)
{
#ifdef WORKING_IPV6
char ipstr[INET6_ADDRSTRLEN];
#else
char ipstr[INET_ADDRSTRLEN];
#endif
int port;
port = SU_GET_PORT(sa);
#ifdef WORKING_IPV6
if (SU_GET_FAMILY(sa) == AF_INET6) {
inet_ntop(AF_INET6, &sa->sin6.sin6_addr, ipstr, sizeof(ipstr));
dbprintf("(sockaddr_in6 *)%p = { %d, %d, %s }\n",
sa,
SU_GET_FAMILY(sa),
port,
ipstr);
} else
#endif
{
inet_ntop(AF_INET, &sa->sin.sin_addr.s_addr, ipstr, sizeof(ipstr));
dbprintf("(sockaddr_in *)%p = { %d, %d, %s }\n",
sa,
SU_GET_FAMILY(sa),
port,
ipstr);
}
}
#ifdef WORKING_IPV6
static char mystr_sockaddr[INET6_ADDRSTRLEN + 20];
#else
static char mystr_sockaddr[INET_ADDRSTRLEN + 20];
#endif
char *
str_sockaddr(
sockaddr_union *sa)
{
#ifdef WORKING_IPV6
char ipstr[INET6_ADDRSTRLEN];
#else
char ipstr[INET_ADDRSTRLEN];
#endif
int port;
port = SU_GET_PORT(sa);
#ifdef WORKING_IPV6
if ( SU_GET_FAMILY(sa) == AF_INET6) {
inet_ntop(AF_INET6, &sa->sin6.sin6_addr, ipstr, sizeof(ipstr));
} else
#endif
{
inet_ntop(AF_INET, &sa->sin.sin_addr.s_addr, ipstr, sizeof(ipstr));
}
g_snprintf(mystr_sockaddr,sizeof(mystr_sockaddr),"%s:%d", ipstr, port);
mystr_sockaddr[sizeof(mystr_sockaddr)-1] = '\0';
return mystr_sockaddr;
}
char *
str_sockaddr_no_port(
sockaddr_union *sa)
{
#ifdef WORKING_IPV6
char ipstr[INET6_ADDRSTRLEN];
#else
char ipstr[INET_ADDRSTRLEN];
#endif
#ifdef WORKING_IPV6
if ( SU_GET_FAMILY(sa) == AF_INET6) {
inet_ntop(AF_INET6, &sa->sin6.sin6_addr, ipstr, sizeof(ipstr));
} else
#endif
{
inet_ntop(AF_INET, &sa->sin.sin_addr.s_addr, ipstr, sizeof(ipstr));
}
g_snprintf(mystr_sockaddr,sizeof(mystr_sockaddr),"%s", ipstr);
mystr_sockaddr[sizeof(mystr_sockaddr)-1] = '\0';
return mystr_sockaddr;
}
char *
str_sockaddr_r(
sockaddr_union *sa,
char *strsockaddr,
socklen_t size)
{
#ifdef WORKING_IPV6
char ipstr[INET6_ADDRSTRLEN];
#else
char ipstr[INET_ADDRSTRLEN];
#endif
int port;
port = SU_GET_PORT(sa);
#ifdef WORKING_IPV6
if ( SU_GET_FAMILY(sa) == AF_INET6) {
inet_ntop(AF_INET6, &sa->sin6.sin6_addr, ipstr, sizeof(ipstr));
} else
#endif
{
inet_ntop(AF_INET, &sa->sin.sin_addr.s_addr, ipstr, sizeof(ipstr));
}
g_snprintf(strsockaddr, size, "%s:%d", ipstr, port);
return strsockaddr;
}
char *
str_sockaddr_no_port_r(
sockaddr_union *sa,
char *strsockaddr,
socklen_t size)
{
#ifdef WORKING_IPV6
char ipstr[INET6_ADDRSTRLEN];
#else
char ipstr[INET_ADDRSTRLEN];
#endif
#ifdef WORKING_IPV6
if ( SU_GET_FAMILY(sa) == AF_INET6) {
inet_ntop(AF_INET6, &sa->sin6.sin6_addr, ipstr, sizeof(ipstr));
} else
#endif
{
inet_ntop(AF_INET, &sa->sin.sin_addr.s_addr, ipstr, sizeof(ipstr));
}
g_snprintf(strsockaddr, size, "%s", ipstr);
return strsockaddr;
}
int
str_to_sockaddr(
const char *src,
sockaddr_union *dst)
{
int result;
g_debug("parsing %s", src);
/* try AF_INET first */
SU_INIT(dst, AF_INET);
if ((result = inet_pton(AF_INET, src, &dst->sin.sin_addr)) == 1)
return result;
/* otherwise try AF_INET6, if supported */
#ifdef WORKING_IPV6
SU_INIT(dst, AF_INET6);
return inet_pton(AF_INET6, src, &dst->sin6.sin6_addr);
#else
return result;
#endif
}
/* Unmap a V4MAPPED IPv6 address into its equivalent IPv4 address. The location
* TMP is used to store the rewritten address, if necessary. Returns a pointer
* to the unmapped address.
*/
#if defined(WORKING_IPV6) && defined(IN6_IS_ADDR_V4MAPPED)
static sockaddr_union *
unmap_v4mapped(
sockaddr_union *sa,
sockaddr_union *tmp)
{
if (SU_GET_FAMILY(sa) == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sa->sin6.sin6_addr)) {
SU_INIT(tmp, AF_INET);
SU_SET_PORT(tmp, SU_GET_PORT(sa));
/* extract the v4 address from byte 12 of the v6 address */
memcpy(&tmp->sin.sin_addr.s_addr,
&sa->sin6.sin6_addr.s6_addr[12],
sizeof(struct in_addr));
return tmp;
}
return sa;
}
#else
/* nothing to do if no IPv6 */
#define unmap_v4mapped(sa, tmp) ((void)tmp, sa)
#endif
int
cmp_sockaddr(
sockaddr_union *ss1,
sockaddr_union *ss2,
int addr_only)
{
sockaddr_union tmp1, tmp2;
/* if addresses are v4mapped, "unmap" them */
ss1 = unmap_v4mapped(ss1, &tmp1);
ss2 = unmap_v4mapped(ss2, &tmp2);
if (SU_GET_FAMILY(ss1) == SU_GET_FAMILY(ss2)) {
if (addr_only) {
#ifdef WORKING_IPV6
if(SU_GET_FAMILY(ss1) == AF_INET6)
return memcmp(
&ss1->sin6.sin6_addr,
&ss2->sin6.sin6_addr,
sizeof(ss1->sin6.sin6_addr));
else
#endif
return memcmp(
&ss1->sin.sin_addr,
&ss2->sin.sin_addr,
sizeof(ss1->sin.sin_addr));
} else {
return memcmp(ss1, ss2, SS_LEN(ss1));
}
} else {
/* compare families to give a total order */
if (SU_GET_FAMILY(ss1) < SU_GET_FAMILY(ss2))
return -1;
else
return 1;
}
}