summaryrefslogtreecommitdiff
path: root/source3/lib/util_sock.c
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2007-10-10 18:25:16 -0700
committerJeremy Allison <jra@samba.org>2007-10-10 18:25:16 -0700
commit8e54530b52fd256137740107e9fdf000f00a7a30 (patch)
treef9ca56cc0b2eff78c3550c924c79ee4ca0666fd2 /source3/lib/util_sock.c
parent0ec55a246238b6cfb3727942c20cd55a16ab4d4a (diff)
downloadsamba-8e54530b52fd256137740107e9fdf000f00a7a30.tar.gz
samba-8e54530b52fd256137740107e9fdf000f00a7a30.tar.bz2
samba-8e54530b52fd256137740107e9fdf000f00a7a30.zip
Add start of IPv6 implementation. Currently most of this is avoiding
IPv6 in winbindd, but moves most of the socket functions that were wrongly in lib/util.c into lib/util_sock.c and provides generic IPv4/6 independent versions of most things. Still lots of work to do, but now I can see how I'll fix the access check code. Nasty part that remains is the name resolution code which is used to returning arrays of in_addr structs. Jeremy. (This used to be commit 3f6bd0e1ec5cc6670f3d08f76fc2cd94c9cd1a08)
Diffstat (limited to 'source3/lib/util_sock.c')
-rw-r--r--source3/lib/util_sock.c515
1 files changed, 466 insertions, 49 deletions
diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c
index 1508ddfce3..7a1a05ec29 100644
--- a/source3/lib/util_sock.c
+++ b/source3/lib/util_sock.c
@@ -29,33 +29,353 @@ static int client_fd = -1;
static char client_ip_string[INET6_ADDRSTRLEN];
/****************************************************************************
- Pritn out an IPv4 or IPv6 address from a struct sockaddr_storage.
+ Return true if a string could be a pure IPv4 address.
****************************************************************************/
-char *print_sockaddr(char *dest,
- size_t destlen,
- struct sockaddr_storage *psa)
+bool is_ipaddress_v4(const char *str)
{
- if (destlen > 0) {
- dest[0] = '\0';
+ bool pure_address = true;
+ int i;
+
+ for (i=0; pure_address && str[i]; i++) {
+ if (!(isdigit((int)str[i]) || str[i] == '.')) {
+ pure_address = false;
+ }
+ }
+
+ /* Check that a pure number is not misinterpreted as an IP */
+ pure_address = pure_address && (strchr_m(str, '.') != NULL);
+ return pure_address;
+}
+
+/****************************************************************************
+ Interpret an internet address or name into an IP address in 4 byte form.
+****************************************************************************/
+
+uint32 interpret_addr(const char *str)
+{
+ struct hostent *hp;
+ uint32 res;
+
+ if (strcmp(str,"0.0.0.0") == 0)
+ return(0);
+ if (strcmp(str,"255.255.255.255") == 0)
+ return(0xFFFFFFFF);
+
+ /* if it's in the form of an IP address then
+ * get the lib to interpret it */
+ if (is_ipaddress_v4(str)) {
+ res = inet_addr(str);
+ } else {
+ /* otherwise assume it's a network name of some sort and use
+ sys_gethostbyname */
+ if ((hp = sys_gethostbyname(str)) == 0) {
+ DEBUG(3,("sys_gethostbyname: Unknown host. %s\n",str));
+ return 0;
+ }
+
+ if(hp->h_addr == NULL) {
+ DEBUG(3,("sys_gethostbyname: host address is "
+ "invalid for host %s\n",str));
+ return 0;
+ }
+ putip((char *)&res,(char *)hp->h_addr);
+ }
+
+ if (res == (uint32)-1)
+ return(0);
+
+ return(res);
+}
+
+/*******************************************************************
+ A convenient addition to interpret_addr().
+******************************************************************/
+
+struct in_addr *interpret_addr2(const char *str)
+{
+ static struct in_addr ret;
+ uint32 a = interpret_addr(str);
+ ret.s_addr = a;
+ return(&ret);
+}
+
+/*******************************************************************
+ Map a text hostname or IP address (IPv4 or IPv6) into a
+ struct sockaddr_storage.
+******************************************************************/
+
+bool interpret_string_addr(struct sockaddr_storage *pss, const char *str)
+{
+ int ret;
+ struct addrinfo *res = NULL;
+ struct addrinfo hints;
+
+ memset(pss,'\0', sizeof(*pss));
+
+ memset(&hints, '\0', sizeof(hints));
+ /* By default make sure it supports TCP. */
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
+
+ ret = getaddrinfo(str, NULL,
+ &hints,
+ &res);
+
+ if (ret) {
+ DEBUG(3,("interpret_string_addr: getaddrinfo failed for "
+ "name %s [%s]\n",
+ str,
+ gai_strerror(ret) ));
+ return false;
+ }
+
+ /* Copy the first sockaddr. */
+ memcpy(pss, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ return true;
+}
+
+/*******************************************************************
+ Check if an IPv7 is 127.0.0.1
+******************************************************************/
+
+bool is_loopback_ip_v4(struct in_addr ip)
+{
+ struct in_addr a;
+ a.s_addr = htonl(INADDR_LOOPBACK);
+ return(ip.s_addr == a.s_addr);
+}
+
+/*******************************************************************
+ Check if a struct sockaddr_storage is the loopback address.
+******************************************************************/
+
+bool is_loopback_addr(const struct sockaddr_storage *pss)
+{
+#if defined(AF_INET6)
+ if (pss->ss_family == AF_INET) {
+ struct in6_addr *pin6 = &((struct sockaddr_in6 *)pss)->sin6_addr;
+ return IN6_IS_ADDR_LOOPBACK(pin6);
+ }
+#endif
+ if (pss->ss_family == AF_INET) {
+ struct in_addr *pin = &((struct sockaddr_in *)pss)->sin_addr;
+ return is_loopback_ip_v4(*pin);
+ }
+ return false;
+}
+
+/*******************************************************************
+ Check if an IPv4 is 0.0.0.0.
+******************************************************************/
+
+bool is_zero_ip_v4(struct in_addr ip)
+{
+ uint32 a;
+ putip((char *)&a,(char *)&ip);
+ return(a == 0);
+}
+
+/*******************************************************************
+ Check if a struct sockaddr_storage has an unspecified address.
+******************************************************************/
+
+bool is_zero_addr(const struct sockaddr_storage *pss)
+{
+#if defined(AF_INET6)
+ if (pss->ss_family == AF_INET) {
+ struct in6_addr *pin6 = &((struct sockaddr_in6 *)pss)->sin6_addr;
+ return IN6_IS_ADDR_UNSPECIFIED(pin6);
+ }
+#endif
+ if (pss->ss_family == AF_INET) {
+ struct in_addr *pin = &((struct sockaddr_in *)pss)->sin_addr;
+ return is_zero_ip_v4(*pin);
}
+ return false;
+}
+
+/*******************************************************************
+ Set an IP to 0.0.0.0.
+******************************************************************/
+
+void zero_ip_v4(struct in_addr *ip)
+{
+ static bool init;
+ static struct in_addr ipzero;
+
+ if (!init) {
+ ipzero = *interpret_addr2("0.0.0.0");
+ init = true;
+ }
+
+ *ip = ipzero;
+}
+
+/*******************************************************************
+ Are two IPs on the same subnet - IPv4 version ?
+********************************************************************/
+
+bool same_net_v4(struct in_addr ip1,struct in_addr ip2,struct in_addr mask)
+{
+ uint32 net1,net2,nmask;
+
+ nmask = ntohl(mask.s_addr);
+ net1 = ntohl(ip1.s_addr);
+ net2 = ntohl(ip2.s_addr);
+
+ return((net1 & nmask) == (net2 & nmask));
+}
+
+/*******************************************************************
+ Convert an IPv4 struct in_addr to a struct sockaddr_storage.
+********************************************************************/
+
+void in_addr_to_sockaddr_storage(struct sockaddr_storage *ss,
+ struct in_addr ip)
+{
+ struct sockaddr_in *sa = (struct sockaddr_in *)ss;
+ memset(ss, '\0', sizeof(*ss));
+ ss->ss_family = AF_INET;
+ sa->sin_addr = ip;
+}
+
+#ifdef AF_INET6
+/*******************************************************************
+ Convert an IPv6 struct in_addr to a struct sockaddr_storage.
+********************************************************************/
+
+void in6_addr_to_sockaddr_storage(struct sockaddr_storage *ss,
+ struct in6_addr ip)
+{
+ struct sockaddr_in6 *sa = (struct sockaddr_in6 *)ss;
+ memset(ss, '\0', sizeof(*ss));
+ ss->ss_family = AF_INET6;
+ sa->sin6_addr = ip;
+}
+#endif
+
+/*******************************************************************
+ Are two IPs on the same subnet?
+********************************************************************/
+
+bool same_net(const struct sockaddr_storage *ip1,
+ const struct sockaddr_storage *ip2,
+ const struct sockaddr_storage *mask)
+{
+ if (ip1->ss_family != ip2->ss_family) {
+ /* Never on the same net. */
+ return false;
+ }
+
+#ifdef AF_INET6
+ if (ip1->ss_family == AF_INET6) {
+ struct sockaddr_in6 ip1_6 = *(struct sockaddr_in6 *)ip1;
+ struct sockaddr_in6 ip2_6 = *(struct sockaddr_in6 *)ip2;
+ struct sockaddr_in6 mask_6 = *(struct sockaddr_in6 *)mask;
+ char *p1 = (char *)&ip1_6.sin6_addr;
+ char *p2 = (char *)&ip2_6.sin6_addr;
+ char *m = (char *)&mask_6.sin6_addr;
+ int i;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ *p1++ &= *m;
+ *p2++ &= *m;
+ m++;
+ }
+ return (memcmp(&ip1_6.sin6_addr,
+ &ip2_6.sin6_addr,
+ sizeof(struct in6_addr)) == 0);
+ }
+#endif
+ if (ip1->ss_family == AF_INET) {
+ return same_net_v4(((const struct sockaddr_in *)ip1)->sin_addr,
+ ((const struct sockaddr_in *)ip2)->sin_addr,
+ ((const struct sockaddr_in *)mask)->sin_addr);
+ }
+ return false;
+}
+
+/*******************************************************************
+ Are two sockaddr_storage's the same family and address ? Ignore port etc.
+********************************************************************/
+
+bool addr_equal(const struct sockaddr_storage *ip1,
+ const struct sockaddr_storage *ip2)
+{
+ if (ip1->ss_family != ip2->ss_family) {
+ /* Never the same. */
+ return false;
+ }
+
+#ifdef AF_INET6
+ if (ip1->ss_family == AF_INET6) {
+ return (memcmp(&((const struct sockaddr_in6 *)ip1)->sin6_addr,
+ &((const struct sockaddr_in6 *)ip2)->sin6_addr,
+ sizeof(struct in6_addr)) == 0);
+ }
+#endif
+ if (ip1->ss_family == AF_INET) {
+ return (memcmp(&((const struct sockaddr_in *)ip1)->sin_addr,
+ &((const struct sockaddr_in *)ip2)->sin_addr,
+ sizeof(struct in_addr)) == 0);
+ }
+ return false;
+}
+
+
+/****************************************************************************
+ Is an IP address the INADDR_ANY or in6addr_any value ?
+****************************************************************************/
+
+bool is_address_any(const struct sockaddr_storage *psa)
+{
#ifdef AF_INET6
if (psa->ss_family == AF_INET6) {
- inet_ntop(AF_INET6,
- &((struct sockaddr_in6 *)psa)->sin6_addr,
- dest,
- destlen);
+ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)psa;
+ if (memcmp(&in6addr_any,
+ &si6->sin6_addr,
+ sizeof(in6addr_any)) == 0) {
+ return true;
+ }
+ return false;
}
#endif
if (psa->ss_family == AF_INET) {
- inet_ntop(AF_INET,
- &((struct sockaddr_in *)psa)->sin_addr,
- dest,
- destlen);
+ struct sockaddr_in *si = (struct sockaddr_in *)psa;
+ if (si->sin_addr.s_addr == INADDR_ANY) {
+ return true;
+ }
+ return false;
}
+ return false;
+}
+
+/****************************************************************************
+ Print out an IPv4 or IPv6 address from a struct sockaddr_storage.
+****************************************************************************/
+
+char *print_sockaddr(char *dest,
+ size_t destlen,
+ const struct sockaddr_storage *psa,
+ socklen_t psalen)
+{
+ if (destlen > 0) {
+ dest[0] = '\0';
+ }
+ (void)getnameinfo((const struct sockaddr *)psa,
+ psalen,
+ dest, destlen,
+ NULL, 0,
+ NI_NUMERICHOST);
return dest;
}
+/****************************************************************************
+ Set the global client_fd variable.
+****************************************************************************/
+
void client_setfd(int fd)
{
client_fd = fd;
@@ -64,6 +384,10 @@ void client_setfd(int fd)
sizeof(client_ip_string)-1);
}
+/****************************************************************************
+ Return a static string of an IP address (IPv4 or IPv6).
+****************************************************************************/
+
static char *get_socket_addr(int fd)
{
struct sockaddr_storage sa;
@@ -87,9 +411,13 @@ static char *get_socket_addr(int fd)
return addr_buf;
}
- return print_sockaddr(addr_buf, sizeof(addr_buf), &sa);
+ return print_sockaddr(addr_buf, sizeof(addr_buf), &sa, length);
}
+/****************************************************************************
+ Return the port number we've bound to on a socket.
+****************************************************************************/
+
static int get_socket_port(int fd)
{
struct sockaddr_storage sa;
@@ -118,7 +446,7 @@ static int get_socket_port(int fd)
char *client_name(void)
{
- return get_peer_name(client_fd,False);
+ return get_peer_name(client_fd,false);
}
char *client_addr(void)
@@ -142,7 +470,7 @@ int smb_read_error = 0;
Determine if a file descriptor is in fact a socket.
****************************************************************************/
-BOOL is_a_socket(int fd)
+bool is_a_socket(int fd)
{
int v;
socklen_t l;
@@ -247,12 +575,12 @@ void set_socket_options(int fd, const char *options)
int ret=0,i;
int value = 1;
char *p;
- BOOL got_value = False;
+ bool got_value = false;
if ((p = strchr_m(tok,'='))) {
*p = 0;
value = atoi(p+1);
- got_value = True;
+ got_value = true;
}
for (i=0;socket_options[i].name;i++)
@@ -559,7 +887,7 @@ ssize_t write_data(int fd, const char *buffer, size_t N)
Send a keepalive packet (rfc1002).
****************************************************************************/
-BOOL send_keepalive(int client)
+bool send_keepalive(int client)
{
unsigned char buf[4];
@@ -583,7 +911,7 @@ static ssize_t read_smb_length_return_keepalive(int fd,
{
ssize_t len=0;
int msg_type;
- BOOL ok = False;
+ bool ok = false;
while (!ok) {
if (timeout > 0) {
@@ -809,23 +1137,23 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
Checks the MAC on signed packets.
****************************************************************************/
-BOOL receive_smb(int fd, char *buffer, unsigned int timeout)
+bool receive_smb(int fd, char *buffer, unsigned int timeout)
{
if (receive_smb_raw(fd, buffer, timeout, 0) < 0) {
- return False;
+ return false;
}
/* Check the incoming SMB signature. */
- if (!srv_check_sign_mac(buffer, True)) {
+ if (!srv_check_sign_mac(buffer, true)) {
DEBUG(0, ("receive_smb: SMB Signature verification "
"failed on incoming packet!\n"));
if (smb_read_error == 0) {
smb_read_error = READ_BAD_SIG;
}
- return False;
+ return false;
}
- return True;
+ return true;
}
ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
@@ -840,7 +1168,7 @@ ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
}
/* Check the incoming SMB signature. */
- if (!srv_check_sign_mac(*buffer, True)) {
+ if (!srv_check_sign_mac(*buffer, true)) {
DEBUG(0, ("receive_smb: SMB Signature verification failed on "
"incoming packet!\n"));
if (smb_read_error == 0) {
@@ -856,7 +1184,7 @@ ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
Send an smb to a fd.
****************************************************************************/
-BOOL send_smb(int fd, char *buffer)
+bool send_smb(int fd, char *buffer)
{
size_t len;
size_t nwritten=0;
@@ -872,12 +1200,12 @@ BOOL send_smb(int fd, char *buffer)
if (ret <= 0) {
DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
(int)len,(int)ret, strerror(errno) ));
- return False;
+ return false;
}
nwritten += ret;
}
- return True;
+ return true;
}
/****************************************************************************
@@ -888,7 +1216,7 @@ int open_socket_in(int type,
int port,
int dlevel,
uint32 socket_addr,
- BOOL rebind )
+ bool rebind )
{
struct sockaddr_in sock;
int res;
@@ -919,7 +1247,7 @@ int open_socket_in(int type,
if( DEBUGLVL( dlevel ) ) {
dbgtext( "open_socket_in(): setsockopt: " );
dbgtext( "SO_REUSEADDR = %s ",
- val?"True":"False" );
+ val?"true":"false" );
dbgtext( "on port %d failed ", port );
dbgtext( "with error = %s\n", strerror(errno) );
}
@@ -930,7 +1258,7 @@ int open_socket_in(int type,
if( DEBUGLVL( dlevel ) ) {
dbgtext( "open_socket_in(): setsockopt: ");
dbgtext( "SO_REUSEPORT = %s ",
- val?"True":"False" );
+ val?"true":"false" );
dbgtext( "on port %d failed ", port );
dbgtext( "with error = %s\n", strerror(errno) );
}
@@ -984,7 +1312,7 @@ int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
sock_out.sin_family = PF_INET;
/* set it non-blocking */
- set_blocking(res,False);
+ set_blocking(res,false);
DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port));
@@ -1029,7 +1357,7 @@ int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
}
/* set it blocking again */
- set_blocking(res,True);
+ set_blocking(res,true);
return res;
}
@@ -1040,12 +1368,12 @@ int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
of DC's all of which are equivalent for our purposes.
**************************************************************************/
-BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
+bool open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
int timeout, int *fd_index, int *fd)
{
int i, resulting_index, res;
int *sockets;
- BOOL good_connect;
+ bool good_connect;
fd_set r_fds, wr_fds;
struct timeval tv;
@@ -1058,7 +1386,7 @@ BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
sockets = SMB_MALLOC_ARRAY(int, num_addrs);
if (sockets == NULL)
- return False;
+ return false;
resulting_index = -1;
@@ -1069,11 +1397,11 @@ BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
sockets[i] = socket(PF_INET, SOCK_STREAM, 0);
if (sockets[i] < 0)
goto done;
- set_blocking(sockets[i], False);
+ set_blocking(sockets[i], false);
}
connect_again:
- good_connect = False;
+ good_connect = false;
for (i=0; i<num_addrs; i++) {
@@ -1095,7 +1423,7 @@ BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
errno == EAGAIN || errno == EINTR) {
/* These are the error messages that something is
progressing. */
- good_connect = True;
+ good_connect = true;
} else if (errno != 0) {
/* There was a direct error */
close(sockets[i]);
@@ -1180,7 +1508,7 @@ BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs,
if (resulting_index >= 0) {
*fd_index = resulting_index;
*fd = sockets[*fd_index];
- set_blocking(*fd, True);
+ set_blocking(*fd, true);
}
free(sockets);
@@ -1223,7 +1551,7 @@ int open_udp_socket(const char *host, int port)
confirm a hostname lookup to prevent spoof attacks.
******************************************************************/
-static BOOL matchname(char *remotehost,struct in_addr addr)
+static bool matchname(char *remotehost,struct in_addr addr)
{
struct hostent *hp;
int i;
@@ -1231,7 +1559,7 @@ static BOOL matchname(char *remotehost,struct in_addr addr)
if ((hp = sys_gethostbyname(remotehost)) == 0) {
DEBUG(0,("sys_gethostbyname(%s): lookup failure.\n",
remotehost));
- return False;
+ return false;
}
/*
@@ -1246,13 +1574,13 @@ static BOOL matchname(char *remotehost,struct in_addr addr)
&& !strequal(remotehost, "localhost")) {
DEBUG(0,("host name/name mismatch: %s != %s\n",
remotehost, hp->h_name));
- return False;
+ return false;
}
/* Look up the host address in the address list we just got. */
for (i = 0; hp->h_addr_list[i]; i++) {
if (memcmp(hp->h_addr_list[i], (char *)&addr,sizeof(addr)) == 0)
- return True;
+ return true;
}
/*
@@ -1263,14 +1591,14 @@ static BOOL matchname(char *remotehost,struct in_addr addr)
DEBUG(0,("host name/address mismatch: %s != %s\n",
inet_ntoa(addr), hp->h_name));
- return False;
+ return false;
}
/*******************************************************************
Return the DNS name of the remote end of a socket.
******************************************************************/
-char *get_peer_name(int fd, BOOL force_lookup)
+char *get_peer_name(int fd, bool force_lookup)
{
static pstring name_buf;
pstring tmp_name;
@@ -1283,7 +1611,7 @@ char *get_peer_name(int fd, BOOL force_lookup)
situations won't work because many networks don't link dhcp
with dns. To avoid the delay we avoid the lookup if
possible */
- if (!lp_hostname_lookups() && (force_lookup == False)) {
+ if (!lp_hostname_lookups() && (force_lookup == false)) {
return get_peer_addr(fd);
}
@@ -1450,3 +1778,92 @@ out_umask:
return -1;
#endif /* HAVE_UNIXSOCKET */
}
+
+/************************************************************
+ Is this my name ? Needs fixing for IPv6.
+************************************************************/
+
+bool is_myname_or_ipaddr(const char *s)
+{
+ fstring name, dnsname;
+ char *servername;
+
+ if ( !s )
+ return false;
+
+ /* santize the string from '\\name' */
+
+ fstrcpy( name, s );
+
+ servername = strrchr_m( name, '\\' );
+ if ( !servername )
+ servername = name;
+ else
+ servername++;
+
+ /* optimize for the common case */
+
+ if (strequal(servername, global_myname()))
+ return true;
+
+ /* check for an alias */
+
+ if (is_myname(servername))
+ return true;
+
+ /* check for loopback */
+
+ if (strequal(servername, "127.0.0.1"))
+ return true;
+
+ if (strequal(servername, "localhost"))
+ return true;
+
+ /* maybe it's my dns name */
+
+ if ( get_mydnsfullname( dnsname ) )
+ if ( strequal( servername, dnsname ) )
+ return true;
+
+ /* handle possible CNAME records */
+
+ if ( !is_ipaddress_v4( servername ) ) {
+ /* use DNS to resolve the name, but only the first address */
+ struct hostent *hp;
+
+ if (((hp = sys_gethostbyname(name)) != NULL) && (hp->h_addr != NULL)) {
+ struct in_addr return_ip;
+ putip( (char*)&return_ip, (char*)hp->h_addr );
+ fstrcpy( name, inet_ntoa( return_ip ) );
+ servername = name;
+ }
+ }
+
+ /* maybe its an IP address? */
+ if (is_ipaddress_v4(servername)) {
+ struct sockaddr_storage ss;
+ struct iface_struct nics[MAX_INTERFACES];
+ int i, n;
+ struct in_addr ip;
+
+ ip = *interpret_addr2(servername);
+ if (is_zero_ip_v4(ip) || is_loopback_ip_v4(ip)) {
+ return false;
+ }
+
+ in_addr_to_sockaddr_storage(&ss, ip);
+
+ n = get_interfaces(nics, MAX_INTERFACES);
+ for (i=0; i<n; i++) {
+ if (nics[i].ip.ss_family != AF_INET) {
+ continue;
+ }
+ if (addr_equal(&nics[i].ip, &ss)) {
+ return true;
+ }
+ }
+ }
+
+ /* no match */
+ return false;
+}