106c1483markm /*
206c1483markm  * Workarounds for known system software bugs. This module provides wrappers
306c1483markm  * around library functions and system calls that are known to have problems
406c1483markm  * on some systems. Most of these workarounds won't do any harm on regular
506c1483markm  * systems.
606c1483markm  *
706c1483markm  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
8225d233shin  *
9225d233shin  * $FreeBSD$
1006c1483markm  */
1106c1483markm
1206c1483markm#ifndef lint
1306c1483markmchar    sccsid[] = "@(#) workarounds.c 1.6 96/03/19 16:22:25";
1406c1483markm#endif
1506c1483markm
1606c1483markm#include <sys/types.h>
1706c1483markm#include <sys/param.h>
1806c1483markm#include <sys/socket.h>
1906c1483markm#include <netinet/in.h>
2006c1483markm#include <arpa/inet.h>
2106c1483markm#include <netdb.h>
2206c1483markm#include <errno.h>
2306c1483markm#include <stdio.h>
2406c1483markm#include <syslog.h>
2506c1483markm#include <string.h>
2606c1483markm
2706c1483markmextern int errno;
2806c1483markm
2906c1483markm#include "tcpd.h"
3006c1483markm
3106c1483markm /*
3206c1483markm  * Some AIX versions advertise a too small MAXHOSTNAMELEN value (32).
3306c1483markm  * Result: long hostnames would be truncated, and connections would be
3406c1483markm  * dropped because of host name verification failures. Adrian van Bloois
3506c1483markm  * (A.vanBloois@info.nic.surfnet.nl) figured out what was the problem.
3606c1483markm  */
3706c1483markm
3806c1483markm#if (MAXHOSTNAMELEN < 64)
3906c1483markm#undef MAXHOSTNAMELEN
4006c1483markm#endif
4106c1483markm
4206c1483markm/* In case not defined in <sys/param.h>. */
4306c1483markm
4406c1483markm#ifndef MAXHOSTNAMELEN
4506c1483markm#define MAXHOSTNAMELEN  256             /* storage for host name */
4606c1483markm#endif
4706c1483markm
4806c1483markm /*
4906c1483markm  * Some DG/UX inet_addr() versions return a struct/union instead of a long.
5006c1483markm  * You have this problem when the compiler complains about illegal lvalues
5106c1483markm  * or something like that. The following code fixes this mutant behaviour.
5206c1483markm  * It should not be enabled on "normal" systems.
5306c1483markm  *
5406c1483markm  * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander).
5506c1483markm  */
5606c1483markm
5706c1483markm#ifdef INET_ADDR_BUG
5806c1483markm
5906c1483markm#undef inet_addr
6006c1483markm
6106c1483markmlong    fix_inet_addr(string)
6206c1483markmchar   *string;
6306c1483markm{
6406c1483markm    return (inet_addr(string).s_addr);
6506c1483markm}
6606c1483markm
6706c1483markm#endif /* INET_ADDR_BUG */
6806c1483markm
6906c1483markm /*
7006c1483markm  * With some System-V versions, the fgets() library function does not
7106c1483markm  * account for partial reads from e.g. sockets. The result is that fgets()
7206c1483markm  * gives up too soon, causing username lookups to fail. Problem first
7306c1483markm  * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>.
7406c1483markm  * The following code works around the problem. It does no harm on "normal"
7506c1483markm  * systems.
7606c1483markm  */
7706c1483markm
7806c1483markm#ifdef BROKEN_FGETS
7906c1483markm
8006c1483markm#undef fgets
8106c1483markm
8206c1483markmchar   *fix_fgets(buf, len, fp)
8306c1483markmchar   *buf;
8406c1483markmint     len;
8506c1483markmFILE   *fp;
8606c1483markm{
8706c1483markm    char   *cp = buf;
8806c1483markm    int     c;
8906c1483markm
9006c1483markm    /*
9106c1483markm     * Copy until the buffer fills up, until EOF, or until a newline is
9206c1483markm     * found.
9306c1483markm     */
9406c1483markm    while (len > 1 && (c = getc(fp)) != EOF) {
9506c1483markm	len--;
9606c1483markm	*cp++ = c;
9706c1483markm	if (c == '\n')
9806c1483markm	    break;
9906c1483markm    }
10006c1483markm
10106c1483markm    /*
10206c1483markm     * Return 0 if nothing was read. This is correct even when a silly buffer
10306c1483markm     * length was specified.
10406c1483markm     */
10506c1483markm    if (cp > buf) {
10606c1483markm	*cp = 0;
10706c1483markm	return (buf);
10806c1483markm    } else {
10906c1483markm	return (0);
11006c1483markm    }
11106c1483markm}
11206c1483markm
11306c1483markm#endif /* BROKEN_FGETS */
11406c1483markm
11506c1483markm /*
11606c1483markm  * With early SunOS 5 versions, recvfrom() does not completely fill in the
11706c1483markm  * source address structure when doing a non-destructive read. The following
11806c1483markm  * code works around the problem. It does no harm on "normal" systems.
11906c1483markm  */
12006c1483markm
12106c1483markm#ifdef RECVFROM_BUG
12206c1483markm
12306c1483markm#undef recvfrom
12406c1483markm
12506c1483markmint     fix_recvfrom(sock, buf, buflen, flags, from, fromlen)
12606c1483markmint     sock;
12706c1483markmchar   *buf;
12806c1483markmint     buflen;
12906c1483markmint     flags;
13006c1483markmstruct sockaddr *from;
13106c1483markmint    *fromlen;
13206c1483markm{
13306c1483markm    int     ret;
13406c1483markm
13506c1483markm    /* Assume that both ends of a socket belong to the same address family. */
13606c1483markm
13706c1483markm    if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) {
13806c1483markm	if (from->sa_family == 0) {
13906c1483markm	    struct sockaddr my_addr;
14006c1483markm	    int     my_addr_len = sizeof(my_addr);
14106c1483markm
14206c1483markm	    if (getsockname(0, &my_addr, &my_addr_len)) {
14306c1483markm		tcpd_warn("getsockname: %m");
14406c1483markm	    } else {
14506c1483markm		from->sa_family = my_addr.sa_family;
14606c1483markm	    }
14706c1483markm	}
14806c1483markm    }
14906c1483markm    return (ret);
15006c1483markm}
15106c1483markm
15206c1483markm#endif /* RECVFROM_BUG */
15306c1483markm
15406c1483markm /*
15506c1483markm  * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an
15606c1483markm  * error in case of a datagram-oriented socket. Instead, they claim that all
15706c1483markm  * UDP requests come from address 0.0.0.0. The following code works around
15806c1483markm  * the problem. It does no harm on "normal" systems.
15906c1483markm  */
16006c1483markm
16106c1483markm#ifdef GETPEERNAME_BUG
16206c1483markm
16306c1483markm#undef getpeername
16406c1483markm
16506c1483markmint     fix_getpeername(sock, sa, len)
16606c1483markmint     sock;
16706c1483markmstruct sockaddr *sa;
16806c1483markmint    *len;
16906c1483markm{
17006c1483markm    int     ret;
171225d233shin#ifdef INET6
172225d233shin    struct sockaddr *sin = sa;
173225d233shin#else
17406c1483markm    struct sockaddr_in *sin = (struct sockaddr_in *) sa;
175225d233shin#endif
17606c1483markm
17706c1483markm    if ((ret = getpeername(sock, sa, len)) >= 0
178225d233shin#ifdef INET6
179225d233shin	&& ((sin->su_si.si_family == AF_INET6
180225d233shin	     && IN6_IS_ADDR_UNSPECIFIED(&sin->su_sin6.sin6_addr))
181225d233shin	    || (sin->su_si.si_family == AF_INET
182225d233shin		&& sin->su_sin.sin_addr.s_addr == 0))) {
183225d233shin#else
18406c1483markm	&& sa->sa_family == AF_INET
18506c1483markm	&& sin->sin_addr.s_addr == 0) {
186225d233shin#endif
18706c1483markm	errno = ENOTCONN;
18806c1483markm	return (-1);
18906c1483markm    } else {
19006c1483markm	return (ret);
19106c1483markm    }
19206c1483markm}
19306c1483markm
19406c1483markm#endif /* GETPEERNAME_BUG */
19506c1483markm
19606c1483markm /*
19706c1483markm  * According to Karl Vogel (vogelke@c-17igp.wpafb.af.mil) some Pyramid
19806c1483markm  * versions have no yp_default_domain() function. We use getdomainname()
19906c1483markm  * instead.
20006c1483markm  */
20106c1483markm
20206c1483markm#ifdef USE_GETDOMAIN
20306c1483markm
20406c1483markmint     yp_get_default_domain(ptr)
20506c1483markmchar  **ptr;
20606c1483markm{
20706c1483markm    static char mydomain[MAXHOSTNAMELEN];
20806c1483markm
20906c1483markm    *ptr = mydomain;
21006c1483markm    return (getdomainname(mydomain, MAXHOSTNAMELEN));
21106c1483markm}
21206c1483markm
21306c1483markm#endif /* USE_GETDOMAIN */
21406c1483markm
21506c1483markm#ifndef INADDR_NONE
21606c1483markm#define INADDR_NONE 0xffffffff
21706c1483markm#endif
21806c1483markm
21906c1483markm /*
22006c1483markm  * Solaris 2.4 gethostbyname() has problems with multihomed hosts. When
22106c1483markm  * doing DNS through NIS, only one host address ends up in the address list.
22206c1483markm  * All other addresses end up in the hostname alias list, interspersed with
22306c1483markm  * copies of the official host name. This would wreak havoc with tcpd's
22406c1483markm  * hostname double checks. Below is a workaround that should do no harm when
22506c1483markm  * accidentally left in. A side effect of the workaround is that address
22606c1483markm  * list members are no longer properly aligned for structure access.
22706c1483markm  */
22806c1483markm
22906c1483markm#ifdef SOLARIS_24_GETHOSTBYNAME_BUG
23006c1483markm
23106c1483markm#undef gethostbyname
23206c1483markm
23306c1483markmstruct hostent *fix_gethostbyname(name)
23406c1483markmchar   *name;
23506c1483markm{
23606c1483markm    struct hostent *hp;
23706c1483markm    struct in_addr addr;
23806c1483markm    char  **o_addr_list;
23906c1483markm    char  **o_aliases;
24006c1483markm    char  **n_addr_list;
24106c1483markm    int     broken_gethostbyname = 0;
24206c1483markm
24306c1483markm    if ((hp = gethostbyname(name)) && !hp->h_addr_list[1] && hp->h_aliases[1]) {
24406c1483markm	for (o_aliases = n_addr_list = hp->h_aliases; *o_aliases; o_aliases++) {
24506c1483markm	    if ((addr.s_addr = inet_addr(*o_aliases)) != INADDR_NONE) {
24606c1483markm		memcpy(*n_addr_list++, (char *) &addr, hp->h_length);
24706c1483markm		broken_gethostbyname = 1;
24806c1483markm	    }
24906c1483markm	}
25006c1483markm	if (broken_gethostbyname) {
25106c1483markm	    o_addr_list = hp->h_addr_list;
25206c1483markm	    memcpy(*n_addr_list++, *o_addr_list, hp->h_length);
25306c1483markm	    *n_addr_list = 0;
25406c1483markm	    hp->h_addr_list = hp->h_aliases;
25506c1483markm	    hp->h_aliases = o_addr_list + 1;
25606c1483markm	}
25706c1483markm    }
25806c1483markm    return (hp);
25906c1483markm}
26006c1483markm
26106c1483markm#endif /* SOLARIS_24_GETHOSTBYNAME_BUG */
26206c1483markm
26306c1483markm /*
26406c1483markm  * Horror! Some FreeBSD 2.0 libc routines call strtok(). Since tcpd depends
26506c1483markm  * heavily on strtok(), strange things may happen. Workaround: use our
26606c1483markm  * private strtok(). This has been fixed in the meantime.
26706c1483markm  */
26806c1483markm
26906c1483markm#ifdef USE_STRSEP
27006c1483markm
27106c1483markmchar   *fix_strtok(buf, sep)
27206c1483markmchar   *buf;
27306c1483markmchar   *sep;
27406c1483markm{
27506c1483markm    static char *state;
27606c1483markm    char   *result;
27706c1483markm
27806c1483markm    if (buf)
27906c1483markm	state = buf;
28006c1483markm    while ((result = strsep(&state, sep)) && result[0] == 0)
28106c1483markm	 /* void */ ;
28206c1483markm    return (result);
28306c1483markm}
28406c1483markm
28506c1483markm#endif /* USE_STRSEP */
28606c1483markm
28706c1483markm /*
28806c1483markm  * IRIX 5.3 (and possibly earlier versions, too) library routines call the
28906c1483markm  * non-reentrant strtok() library routine, causing hosts to slip through
29006c1483markm  * allow/deny filters. Workaround: don't rely on the vendor and use our own
29106c1483markm  * strtok() function. FreeBSD 2.0 has a similar problem (fixed in 2.0.5).
29206c1483markm  */
29306c1483markm
29406c1483markm#ifdef LIBC_CALLS_STRTOK
29506c1483markm
29606c1483markmchar   *my_strtok(buf, sep)
29706c1483markmchar   *buf;
29806c1483markmchar   *sep;
29906c1483markm{
30006c1483markm    static char *state;
30106c1483markm    char   *result;
30206c1483markm
30306c1483markm    if (buf)
30406c1483markm	state = buf;
30506c1483markm
30606c1483markm    /*
30706c1483markm     * Skip over separator characters and detect end of string.
30806c1483markm     */
30906c1483markm    if (*(state += strspn(state, sep)) == 0)
31006c1483markm	return (0);
31106c1483markm
31206c1483markm    /*
31306c1483markm     * Skip over non-separator characters and terminate result.
31406c1483markm     */
31506c1483markm    result = state;
31606c1483markm    if (*(state += strcspn(state, sep)) != 0)
31706c1483markm	*state++ = 0;
31806c1483markm    return (result);
31906c1483markm}
32006c1483markm
32106c1483markm#endif /* LIBC_CALLS_STRTOK */
322