sockets: fix GET-HOST-BY-NAME and GET-HOST-BY-ADDRESS

After the recent refactor of GET-HOST-BY-NAME to use getaddrinfo,
these functions had multiple problems that are fixed in this commit:
- in GET-HOST-BY-NAME, freeaddrinfo was called on an unitialized
  struct addrinfo if getaddrinfo failed
- error handling in GET-HOST-BY-NAME wasn't working, we were still
  trying to use the error handling for gethostbyname
- GET-HOST-BY-ADDRESS still used the deprecated gethostbyaddr function
This commit is contained in:
Marius Gerbershagen 2020-01-25 16:27:38 +01:00
parent eadefe86ed
commit 12cac13786

View file

@ -141,6 +141,23 @@
#+:wsock
(defconstant +af-named-pipe+ -2)
(Clines
"
static void fill_inet_sockaddr(struct sockaddr_in *sockaddr, int port,
int a1, int a2, int a3, int a4)
{
#if defined(_MSC_VER) || defined(mingw32)
memset(sockaddr,0,sizeof(struct sockaddr_in));
#else
bzero(sockaddr,sizeof(struct sockaddr_in));
#endif
sockaddr->sin_family = AF_INET;
sockaddr->sin_port = htons(port);
sockaddr->sin_addr.s_addr= htonl((uint32_t)a1<<24 | (uint32_t)a2<<16 | (uint32_t)a3<<8 | (uint32_t)a4) ;
}
")
;; Foreign functions
(defentry ff-socket (:int :int :int) (:int "socket") :no-interrupts t)
@ -192,12 +209,18 @@ containing the whole rest of the given `string', if any."
"Returns a HOST-ENT instance for HOST-NAME or throws some kind of condition.
HOST-NAME may also be an IP address in dotted quad notation or some
other weird stuff - see getaddrinfo(3) for details."
(multiple-value-bind (result canonical-name addresses aliases)
(multiple-value-bind (errno canonical-name addresses aliases)
(c-inline (host-name) (:cstring)
(values :object :object :object :object)
(values :int :object :object :object)
"
{
struct addrinfo hints;
struct addrinfo *result;
cl_object host_name = ECL_NIL;
cl_object aliases = ECL_NIL;
cl_object addresses = ECL_NIL;
int err;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; /* IPv4 */
hints.ai_socktype = 0; /* Any type */
@ -205,13 +228,12 @@ other weird stuff - see getaddrinfo(3) for details."
hints.ai_flags = (AI_ADDRCONFIG | AI_CANONNAME); /* Get cannonname */
hints.ai_addr = NULL;
hints.ai_next = NULL;
struct addrinfo *result;
int s = getaddrinfo(#0, NULL, &hints, &result);
if (s == 0) {
cl_object host_name = ECL_NIL;
cl_object aliases = ECL_NIL;
cl_object addresses = ECL_NIL;
ecl_disable_interrupts();
err = getaddrinfo(#0, NULL, &hints, &result);
ecl_enable_interrupts();
if (err == 0) {
struct addrinfo *rp;
for (rp = result; rp != NULL; rp = rp->ai_next) {
@ -231,86 +253,58 @@ other weird stuff - see getaddrinfo(3) for details."
aliases = CONS(alias, aliases);
}
}
@(return 0) = ECL_T;
@(return 1) = host_name;
@(return 2) = addresses;
@(return 3) = aliases;
freeaddrinfo(result);
}
else {
/* error returned */
@(return 0) = ecl_make_fixnum(s); /* error number */
@(return 1) = ecl_make_simple_base_string(gai_strerror(s),-1); /* error string */
@(return 2) = ECL_NIL;
@(return 3) = ECL_NIL;
}
freeaddrinfo(result);
@(return 0) = err;
@(return 1) = host_name;
@(return 2) = addresses;
@(return 3) = aliases;
}"
:one-liner nil)
(if result
:side-effects t)
(if (= errno 0)
(make-instance 'host-ent
:name (or canonical-name host-name)
:aliases aliases
:type +af-inet+
:addresses addresses)
(name-service-error "get-host-by-name"))))
(name-service-error "get-host-by-name" errno))))
;;; Use values from getnameinfo man page if NI_MAXHOST is not declared
(clines
"#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#endif")
(defun get-host-by-address (address)
(assert (and (typep address 'vector)
(= (length address) 4)))
(let ((host-ent (make-instance 'host-ent)))
(if
(c-inline (address host-ent
#'(setf host-ent-name)
#'(setf host-ent-aliases)
#'(setf host-ent-address-type)
#'(setf host-ent-addresses))
(t t t t t t) t
"
(multiple-value-bind (errno name)
(c-inline ((aref address 0) (aref address 1) (aref address 2) (aref address 3))
(:int :int :int :int) (values :int t)
"
{
unsigned char vector[4];
struct hostent *hostent;
vector[0] = fixint(ecl_aref(#0,0));
vector[1] = fixint(ecl_aref(#0,1));
vector[2] = fixint(ecl_aref(#0,2));
vector[3] = fixint(ecl_aref(#0,3));
ecl_disable_interrupts();
hostent = gethostbyaddr(wincoerce(const char *, vector),4,AF_INET);
ecl_enable_interrupts();
struct sockaddr_in addr;
socklen_t addr_len = (socklen_t)sizeof(struct sockaddr_in);
char host[NI_MAXHOST];
int err;
if (hostent != NULL) {
char **aliases;
char **addrs;
cl_object aliases_list = ECL_NIL;
cl_object addr_list = ECL_NIL;
int length = hostent->h_length;
fill_inet_sockaddr(&addr, 0, #0, #1, #2, #3);
funcall(3,#2,ecl_make_simple_base_string(hostent->h_name,-1),#1);
funcall(3,#4,ecl_make_integer(hostent->h_addrtype),#1);
ecl_disable_interrupts();
err = getnameinfo((struct sockaddr *) &addr, addr_len, host, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
ecl_enable_interrupts();
for (aliases = hostent->h_aliases; *aliases != NULL; aliases++) {
aliases_list = CONS(ecl_make_simple_base_string(*aliases,-1),aliases_list);
}
funcall(3,#3,aliases_list,#1);
for (addrs = hostent->h_addr_list; *addrs != NULL; addrs++) {
int pos;
cl_object vector = funcall(2,@make-array,ecl_make_fixnum(length));
for (pos = 0; pos < length; pos++)
ecl_aset(vector, pos, ecl_make_fixnum((unsigned char)((*addrs)[pos])));
addr_list = CONS(vector, addr_list);
}
funcall(3,#5,addr_list,#1);
@(return) = #1;
} else {
@(return) = ECL_NIL;
}
@(return 0) = err;
@(return 1) = err ? ECL_NIL : ecl_make_simple_base_string(host,-1);
}"
:side-effects t)
host-ent
(name-service-error "get-host-by-address"))))
:side-effects t)
(if (= errno 0)
(make-instance 'host-ent
:name name
:aliases nil
:type +af-inet+
:addresses (list address))
(name-service-error "get-host-by-address" errno))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
@ -636,23 +630,6 @@ Examples:
"Make an INET socket. Deprecated in favour of make-instance"
(make-instance 'inet-socket :type type :protocol protocol))
(Clines
"
static void fill_inet_sockaddr(struct sockaddr_in *sockaddr, int port,
int a1, int a2, int a3, int a4)
{
#if defined(_MSC_VER) || defined(mingw32)
memset(sockaddr,0,sizeof(struct sockaddr_in));
#else
bzero(sockaddr,sizeof(struct sockaddr_in));
#endif
sockaddr->sin_family = AF_INET;
sockaddr->sin_port = htons(port);
sockaddr->sin_addr.s_addr= htonl((uint32_t)a1<<24 | (uint32_t)a2<<16 | (uint32_t)a3<<8 | (uint32_t)a4) ;
}
")
(defmethod socket-bind ((socket inet-socket) &rest address)
@ -1441,22 +1418,12 @@ also known as unix-domain sockets."))
"#define EPROTONOSUPPORT WSAEPROTONOSUPPORT"
"#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT"
"#define ENETUNREACH WSAENETUNREACH"
"#define NETDB_INTERNAL WSAEAFNOSUPPORT"
"#define NETDB_SUCCESS 0"
)
#+:haiku
(clines
"#define ESOCKTNOSUPPORT ENOTSUP")
(Clines
"#ifndef NETDB_INTERNAL"
"#define NETDB_INTERNAL 0"
"#endif"
"#ifndef NETDB_SUCCESS"
"#define NETDB_SUCCESS 0"
"#endif")
(define-socket-condition EADDRINUSE address-in-use-error)
(define-socket-condition EAGAIN interrupted-error)
(define-socket-condition EBADF bad-file-descriptor-error)
@ -1485,19 +1452,17 @@ also known as unix-domain sockets."))
;;; 2) DNS ERRORS
;;;
(defvar *name-service-errno* 0
"The value of h_errno, after it's been fetched from Unix-land by calling
GET-NAME-SERVICE-ERRNO")
(clines
"#ifndef EAI_SYSTEM
#define EAI_SYSTEM 0
#endif")
(defun name-service-error (where)
(get-name-service-errno)
;; Comment next to NETDB_INTERNAL in netdb.h says "See errno.".
;; This special case treatment hasn't actually been tested yet.
(if (= *name-service-errno* (c-constant "NETDB_INTERNAL"))
(defun name-service-error (where &optional (errno #+:wsock (c-constant "WSAGetLastError()")))
(if (= errno (c-constant "EAI_SYSTEM"))
(socket-error where)
(let ((condition
(condition-for-name-service-errno *name-service-errno*)))
(error condition :errno *name-service-errno* :syscall where))))
(let ((condition
(condition-for-name-service-errno errno)))
(error condition :errno errno :syscall where))))
(define-condition name-service-error (condition)
((errno :initform nil
@ -1523,30 +1488,22 @@ GET-NAME-SERVICE-ERRNO")
(defparameter *conditions-for-name-service-errno* nil)
(define-name-service-condition NETDB_INTERNAL netdb-internal-error)
(define-name-service-condition NETDB_SUCCESS netdb-success-error)
(define-name-service-condition HOST_NOT_FOUND host-not-found-error)
(define-name-service-condition TRY_AGAIN try-again-error)
(define-name-service-condition NO_RECOVERY no-recovery-error)
;; this is the same as the next one
;;(define-name-service-condition NO_DATA no-data-error)
(define-name-service-condition NO_ADDRESS no-address-error)
;;; getaddrinfo/getnameinfo have more failure codes, but for
;;; compability with gethostbyname/gethostbyaddr, we only need the
;;; following
(define-name-service-condition EAI_NONAME host-not-found-error)
(define-name-service-condition EAI_AGAIN try-again-error)
(define-name-service-condition EAI_FAIL no-recovery-error)
(defun condition-for-name-service-errno (err)
(or (cdr (assoc err *conditions-for-name-service-errno* :test #'eql))
'name-service))
(defun get-name-service-errno ()
(setf *name-service-errno* (c-constant #-:wsock "h_errno" #+:wsock "WSAGetLastError()")))
(defun get-name-service-error-message (num)
#+:nsr
(c-inline (num) (:int) :cstring "strerror(#0)" :one-liner t)
#+:wsock
(get-win32-error-string num)
#-(or :wsock :nsr)
(c-inline (num) (:int) :cstring "strerror(#0)" :one-liner t)
)
#-:wsock
(c-inline (num) (:int) :cstring "gai_strerror(#0)" :one-liner t))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;