Subject: Re: inet_ntop() prototype question
To: Christoph Kaegi <kgc@zhwin.ch>
From: Greg A. Woods <woods@weird.com>
List: netbsd-users
Date: 04/07/2003 12:06:52
[ On Monday, April 7, 2003 at 09:40:09 (+0200), Christoph Kaegi wrote: ]
> Subject: inet_ntop() prototype question
>
> 
> I'm not sure, if this is the appropriate forum, but
> why is the last parameter of inet_ntop of type socklen_t?
> Stevens has it as size_t, which would make more sense for
> me, as it should specify the length of the buffer in which
> inet_ntop will put an address in presentation format.

POSIX-1003.1-2001 is after Stevens' time.

P1003.1-2001 has:

    const char *inet_ntop(int af, const void *restrict src,                    
                          char *restrict dst, socklen_t size);                                

Another portabilty problem is with the changes that were made by
P1003.1g/P1003.1-2001 to the "buflen" parameters of the like of send()
and sendto() and their conflicting return types.  The POSIX APIs expect
a size_t width byte count parameter, and then return a ssize_t width
byte count and the caller is supposed to expect the returned value to be
the same as the passed value.  That might be possible today on many
platforms, but it will not always be possible in the future.

Below is a hunk of preprocessor spagetti I've come up with that seems to
deal with the worst of at least the parameter type incompatabilities on
the most popular platforms, as well as one or two older platforms.  On
some platforms there can still be some relatively harmless GCC warnings
from a few platforms which have kept the non-standard prototypes for
some of the routines normally supplied by BIND's resolver library, such
as gethostbyaddr(), but have switched to P1003.1-2001 for others
(e.g. FreeBSD and friends).  Don't worry about the warnings though --
thanks to the silly way prototypes were mis-invented to break
compatability with traditional C, ANSI C will have its way with your
parameter types anyway!  ;-)

P1003.1-2001 has:

    struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);

NetBSD, despite still having a BIND-4 based resolver, has partway gone to:

    struct hostent *gethostbyaddr(const char *addr, socklen_t len, int type);

BIND-8.3.4, and thus SunOS-5.9, FreeBSD, Darwin, etc., still have:

    struct hostent *gethostbyaddr(const char *addr, int len, int type);

The type of "len" should of course be a socklen_t, just as with
inet_ntop(), because it is the length of a struct socklen variant, but
BIND has not yet switched to the new P1003.1-2001 API (and NetBSD is not
yet quite complaint either :-).


#if (defined(__SVR4) || defined(__svr4) || defined(SVR4) || defined(svr4)) && !defined(__svr4__)
# define __svr4__	1
#endif

#if defined(__APPLE__) && defined(__MACH__) && !defined(__darwin__)
# define __darwin__	1
#endif

#if !defined(HAVE_INET_ATON) && \
    ((defined(__BIND) && (__BIND - 0) > 19950621) || \
     (defined(__NAMESER) && (__NAMESER - 0) > 19961001) || \
     (defined(BSD4_3) && !defined(BSD4_4)) || \
     (defined(BSD) && (BSD >= 199103)))
# define HAVE_INET_ATON		1
#endif

#ifndef _IPADDR_T
# if defined(__alpha) || defined(__arch64__)
typedef unsigned int	ipaddr_t;
# else
typedef unsigned long	ipaddr_t;
# endif
# define _IPADDR_T	ipaddr_t
#endif

/*
 * FreeBSD (and Darwin in its image) is a bit brain-dead in the way they do
 * this and still follow the ancient 4.4BSD style of using the fact that
 * _BSD_SOCKLEN_T_ is NOT defined in order to typedef socklen_t at the earliest
 * point it's needed.  However they leave no means for applications to know if
 * the typedef has already been done.
 *
 * FYI: In NetBSD socklen_t came into use just before 1.3J:
 *
 *	(__NetBSD_Version__ - 0) > 103100000
 */
#if (defined(__FreeBSD__) || defined(__darwin__)) && defined(_BSD_SOCKLEN_T_)
# include "ERROR: something's wrong with the #includes above!"
#endif
/* Sigh, standards are such wonderful things.... */
#if !defined(socklen_t) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(_SOCKLEN_T) && !defined(__socklen_t_defined)
# if (/* SunOS-4 gcc */defined(__sun__) && !defined(__svr4__)) || \
     (/* SunOS-4 cc */defined(sun) && defined(unix) && !defined(__svr4__)) || \
     (/* 4.3BSD */defined(BSD) && ((BSD - 0) > 0) && ((BSD - 0) < 199506))
typedef int		__socklen_t;	/* 4.3BSD and older */
# else
typedef size_t		__socklen_t;	/* P1003.1g socket-related datum length */
# endif
typedef __socklen_t	socklen_t;
# define socklen_t	__socklen_t
#endif

/*
 * BSD Socket API buffer length type.
 *
 * Deal with the other parts of the P1003.1g API change which the POSIX
 * committee didn't seem to address.  I.e. use this for the "buflen" or "len"
 * parameters of functions such as send(2), sendto(2), recv(2), recvfrom(2),
 * etc.  (not the address length, which should be a socklen_t, just the buffer
 * length).
 *
 * Unfortunately this doesn't deal with the problem that the passed in length
 * is now a size_t width integer, but the returned type is only a ssize_t width
 * integer....  Standards.   Sigh.
 *
 * Perhaps the defined(__sun__) shouldn't be there on the _SOCKLEN_T line....
 */
#if !defined(sock_buflen_t)			/* silly dreamer! */
# if (/* SunOS-4 gcc */defined(__sun__) && !defined(__svr4__)) || \
     (/* SunOS-4 cc */defined(sun) && defined(unix) && !defined(__svr4__)) || \
     (/* SunOS-5.9 */defined(__sun__) && defined(__svr4__) && !defined(_SOCKLEN_T)) || \
     (/* 4.3BSD */defined(BSD) && ((BSD - 0) > 0) && ((BSD - 0) < 199506))
typedef int		__sock_buflen_t;	/* 4.3BSD and older used int */
# else
typedef size_t		__sock_buflen_t;	/* P1003.1g adherents use size_t */
# endif
typedef __sock_buflen_t	sock_buflen_t;
# define sock_buflen_t	__sock_buflen_t
#endif


-- 
								Greg A. Woods

+1 416 218-0098;            <g.a.woods@ieee.org>;           <woods@robohack.ca>
Planix, Inc. <woods@planix.com>; VE3TCP; Secrets of the Weird <woods@weird.com>