/* * netsupport.c - networking support module - implementation * * nc6 - an advanced netcat clone * Copyright (C) 2002-2006 Chris Leishman * Copyright (C) 2001-2006 Mauro Tortonesi * * 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 */ #include "system.h" #include "misc.h" #include "netsupport.h" #include #include #include #include #include #include #include #include #if HAVE_ALLOCA_H #include #else #ifdef _AIX #pragma alloca #else #ifndef alloca /* predefined by HP cc +Olibcalls */ char *alloca(); #endif #endif #endif RCSID("@(#) $Header: /ds6/cvs/nc6/src/netsupport.c,v 1.14 2006/01/19 22:46:23 chris Exp $"); /* call 'connect' in non-blocking mode and use select to await a timeout */ int connect_with_timeout(int fd, const struct sockaddr *sa, socklen_t salen, int timeout) { int err; struct timeval tv, *tvp = NULL; fd_set connect_fdset; socklen_t len; int optval; assert(sa != NULL); assert(salen > 0); /* set connect timeout */ if (timeout > 0) { tv.tv_sec = (time_t)timeout; tv.tv_usec = 0; tvp = &tv; } /* set fd to nonblocking */ nonblock(fd); /* attempt the connection */ err = connect(fd, sa, salen); if (err != 0 && errno == EINPROGRESS) { /* connection is proceeding * it is complete (or failed) when select returns */ /* initialize connect_fdset */ FD_ZERO(&connect_fdset); FD_SET(fd, &connect_fdset); /* call select */ do { err = select(fd + 1, NULL, &connect_fdset, NULL, tvp); } while (err < 0 && errno == EINTR); /* select error */ if (err < 0) return -1; /* we have reached a timeout */ if (err == 0) { errno = ETIMEDOUT; return -1; } /* select returned successfully, but we must test socket * error for result */ len = sizeof(optval); err = getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &len); if (err != 0) return err; /* if getsockopt indicates a failure occured, then store the * error code into errno and return a failure */ if (optval != 0) { errno = optval; return -1; } } return 0; } #ifdef ENABLE_IPV6 /* returns true if a represents an ipv4-mapped address */ bool is_address_ipv4_mapped(const struct sockaddr *a) { bool ret = false; const struct sockaddr_in6 *tmp = (const struct sockaddr_in6 *)a; assert(a != NULL); if ((a->sa_family == AF_INET6) && IN6_IS_ADDR_V4MAPPED(&(tmp->sin6_addr))) { ret = true; } return ret; } #endif /* compare two sockaddr structs to see if they represent the same address */ bool sockaddr_compare(const struct sockaddr *a, socklen_t a_len, const struct sockaddr *b, socklen_t b_len) { assert(a != NULL); assert(b != NULL); assert(a_len > 0); assert(b_len > 0); #ifdef ENABLE_IPV6 /* we have to handle IPv6 IPV4MAPPED addresses - convert them to IPv4 */ if (is_address_ipv4_mapped(a)) { const struct sockaddr_in6 *a6; struct sockaddr_in *a_in; a6 = (const struct sockaddr_in6 *)a; a_in = (struct sockaddr_in *)alloca(sizeof(struct sockaddr_in)); memset(a_in, 0, sizeof(struct sockaddr_in)); memcpy(&(a_in->sin_addr.s_addr), &(a6->sin6_addr.s6_addr[12]), sizeof(struct in_addr)); a_in->sin_port = a6->sin6_port; a = (const struct sockaddr *)a_in; } if (is_address_ipv4_mapped(b)) { const struct sockaddr_in6 *b6; struct sockaddr_in *b_in; b6 = (const struct sockaddr_in6 *)b; b_in = (struct sockaddr_in *)alloca(sizeof(struct sockaddr_in)); memset(b_in, 0, sizeof(struct sockaddr_in)); memcpy(&(b_in->sin_addr.s_addr), &(b6->sin6_addr.s6_addr[12]), sizeof(struct in_addr)); b_in->sin_port = b6->sin6_port; b = (const struct sockaddr *)b_in; } #endif /* now we can perform the comparison */ /* check family is the same */ if (a->sa_family != b->sa_family) return false; /* the comparison is unique for each real sockaddr family */ #ifdef ENABLE_IPV6 if (a->sa_family == AF_INET6) { const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *)a; const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *)b; #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID /* compare scope */ if (a6->sin6_scope_id && b6->sin6_scope_id && (a6->sin6_scope_id != b6->sin6_scope_id)) { return false; } #endif /* compare address part * either may be IN6ADDR_ANY, resulting in a good match */ if ((memcmp(&(a6->sin6_addr), &in6addr_any, sizeof(struct in6_addr)) != 0) && (memcmp(&(b6->sin6_addr), &in6addr_any, sizeof(struct in6_addr)) != 0) && (memcmp(&(a6->sin6_addr), &(b6->sin6_addr), sizeof(struct in6_addr)) != 0)) { return false; } /* compare port part * either port may be 0(any), resulting in a good match */ return ((a6->sin6_port == 0) || (b6->sin6_port == 0) || (a6->sin6_port == b6->sin6_port))? true : false; } #endif if (a->sa_family == AF_INET) { const struct sockaddr_in *a_in = (const struct sockaddr_in *)a; const struct sockaddr_in *b_in = (const struct sockaddr_in *)b; /* compare address part * either may be INADDR_ANY, resulting in a good match */ if ((a_in->sin_addr.s_addr != INADDR_ANY) && (b_in->sin_addr.s_addr != INADDR_ANY) && (a_in->sin_addr.s_addr != b_in->sin_addr.s_addr)) { return false; } /* compare port part */ /* either port may be 0(any), resulting in a good match */ return ((a_in->sin_port == 0) || (b_in->sin_port == 0) || (a_in->sin_port == b_in->sin_port))? true : false; } #ifdef ENABLE_BLUEZ if (a->sa_family == AF_BLUETOOTH) { /* just compare the sockaddr structures directly */ return ((a_len == b_len) && memcmp(a, b, a_len) == 0)? true : false; } #endif /* for all other socket types, return false */ return false; } /* On some systems, getaddrinfo will return results that can't actually be * used - resulting in a failure when trying to create the socket. * This function checks for all the different error codes that indicate this * situation */ bool unsupported_sock_error(int err) { return (err == EPFNOSUPPORT || err == EAFNOSUPPORT || err == EPROTONOSUPPORT || err == ESOCKTNOSUPPORT || err == ENOPROTOOPT)? true : false; } /* add a new fd/socktype pair to the list */ bound_socket_t *add_bound_socket(bound_socket_t *list, int fd, int socktype) { bound_socket_t *fdnew; fdnew = (bound_socket_t *)xmalloc(sizeof(bound_socket_t)); fdnew->fd = fd; fdnew->socktype = socktype; /* prepend to the start of the list */ fdnew->next = list; return fdnew; } /* get the socktype for a given fd in a list */ int get_bound_socket_type(const bound_socket_t *list, int fd) { assert(list != NULL); while (list != NULL && list->fd != fd) list = list->next; return ((list != NULL) ? list->socktype : -1); } /* free a bound socket list */ void free_bound_sockets(bound_socket_t *list) { bound_socket_t *tmp; while (list != NULL) { tmp = list; list = list->next; free(tmp); } } /* close all bound sockets in a list and free the list */ void close_and_destroy_bound_sockets(bound_socket_t *list) { bound_socket_t *tmp; while (list != NULL) { tmp = list; list = list->next; close(tmp->fd); free(tmp); } }