2N/A/*
2N/A * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A /*
2N/A * Workarounds for known system software bugs. This module provides wrappers
2N/A * around library functions and system calls that are known to have problems
2N/A * on some systems. Most of these workarounds won't do any harm on regular
2N/A * systems.
2N/A *
2N/A * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
2N/A */
2N/A
2N/A#ifndef lint
2N/Achar sccsid[] = "@(#) workarounds.c 1.6 96/03/19 16:22:25";
2N/A#endif
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/param.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <netdb.h>
2N/A#include <errno.h>
2N/A#include <stdio.h>
2N/A#include <syslog.h>
2N/A#include <string.h>
2N/A
2N/Aextern int errno;
2N/A
2N/A#include "tcpd.h"
2N/A
2N/A /*
2N/A * Some AIX versions advertise a too small MAXHOSTNAMELEN value (32).
2N/A * Result: long hostnames would be truncated, and connections would be
2N/A * dropped because of host name verification failures. Adrian van Bloois
2N/A * (A.vanBloois@info.nic.surfnet.nl) figured out what was the problem.
2N/A */
2N/A
2N/A#if (MAXHOSTNAMELEN < 64)
2N/A#undef MAXHOSTNAMELEN
2N/A#endif
2N/A
2N/A/* In case not defined in <sys/param.h>. */
2N/A
2N/A#ifndef MAXHOSTNAMELEN
2N/A#define MAXHOSTNAMELEN 256 /* storage for host name */
2N/A#endif
2N/A
2N/A /*
2N/A * Some DG/UX inet_addr() versions return a struct/union instead of a long.
2N/A * You have this problem when the compiler complains about illegal lvalues
2N/A * or something like that. The following code fixes this mutant behaviour.
2N/A * It should not be enabled on "normal" systems.
2N/A *
2N/A * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander).
2N/A */
2N/A
2N/A#ifdef INET_ADDR_BUG
2N/A
2N/A#undef inet_addr
2N/A
2N/Along fix_inet_addr(string)
2N/Achar *string;
2N/A{
2N/A return (inet_addr(string).s_addr);
2N/A}
2N/A
2N/A#endif /* INET_ADDR_BUG */
2N/A
2N/A /*
2N/A * With some System-V versions, the fgets() library function does not
2N/A * account for partial reads from e.g. sockets. The result is that fgets()
2N/A * gives up too soon, causing username lookups to fail. Problem first
2N/A * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>.
2N/A * The following code works around the problem. It does no harm on "normal"
2N/A * systems.
2N/A */
2N/A
2N/A#ifdef BROKEN_FGETS
2N/A
2N/A#undef fgets
2N/A
2N/Achar *fix_fgets(buf, len, fp)
2N/Achar *buf;
2N/Aint len;
2N/AFILE *fp;
2N/A{
2N/A char *cp = buf;
2N/A int c;
2N/A
2N/A /*
2N/A * Copy until the buffer fills up, until EOF, or until a newline is
2N/A * found.
2N/A */
2N/A while (len > 1 && (c = getc(fp)) != EOF) {
2N/A len--;
2N/A *cp++ = c;
2N/A if (c == '\n')
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * Return 0 if nothing was read. This is correct even when a silly buffer
2N/A * length was specified.
2N/A */
2N/A if (cp > buf) {
2N/A *cp = 0;
2N/A return (buf);
2N/A } else {
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A#endif /* BROKEN_FGETS */
2N/A
2N/A /*
2N/A * With early SunOS 5 versions, recvfrom() does not completely fill in the
2N/A * source address structure when doing a non-destructive read. The following
2N/A * code works around the problem. It does no harm on "normal" systems.
2N/A */
2N/A
2N/A#ifdef RECVFROM_BUG
2N/A
2N/A#undef recvfrom
2N/A
2N/Aint fix_recvfrom(sock, buf, buflen, flags, from, fromlen)
2N/Aint sock;
2N/Achar *buf;
2N/Aint buflen;
2N/Aint flags;
2N/Astruct sockaddr *from;
2N/Aint *fromlen;
2N/A{
2N/A int ret;
2N/A
2N/A /* Assume that both ends of a socket belong to the same address family. */
2N/A
2N/A if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) {
2N/A if (from->sa_family == 0) {
2N/A struct sockaddr my_addr;
2N/A int my_addr_len = sizeof(my_addr);
2N/A
2N/A if (getsockname(0, &my_addr, &my_addr_len)) {
2N/A tcpd_warn("getsockname: %m");
2N/A } else {
2N/A from->sa_family = my_addr.sa_family;
2N/A }
2N/A }
2N/A }
2N/A return (ret);
2N/A}
2N/A
2N/A#endif /* RECVFROM_BUG */
2N/A
2N/A /*
2N/A * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an
2N/A * error in case of a datagram-oriented socket. Instead, they claim that all
2N/A * UDP requests come from address 0.0.0.0. The following code works around
2N/A * the problem. It does no harm on "normal" systems.
2N/A */
2N/A
2N/A#ifdef GETPEERNAME_BUG
2N/A
2N/A#undef getpeername
2N/A
2N/Aint fix_getpeername(sock, sa, len)
2N/Aint sock;
2N/Astruct sockaddr *sa;
2N/Asocklen_t *len;
2N/A{
2N/A int ret;
2N/A struct sockaddr_in *sin = (struct sockaddr_in *) sa;
2N/A
2N/A if ((ret = getpeername(sock, sa, len)) >= 0
2N/A && sa->sa_family == AF_INET
2N/A && sin->sin_addr.s_addr == 0) {
2N/A errno = ENOTCONN;
2N/A return (-1);
2N/A } else {
2N/A return (ret);
2N/A }
2N/A}
2N/A
2N/A#endif /* GETPEERNAME_BUG */
2N/A
2N/A /*
2N/A * According to Karl Vogel (vogelke@c-17igp.wpafb.af.mil) some Pyramid
2N/A * versions have no yp_default_domain() function. We use getdomainname()
2N/A * instead.
2N/A */
2N/A
2N/A#ifdef USE_GETDOMAIN
2N/A
2N/Aint yp_get_default_domain(ptr)
2N/Achar **ptr;
2N/A{
2N/A static char mydomain[MAXHOSTNAMELEN];
2N/A
2N/A *ptr = mydomain;
2N/A return (getdomainname(mydomain, MAXHOSTNAMELEN));
2N/A}
2N/A
2N/A#endif /* USE_GETDOMAIN */
2N/A
2N/A#ifndef INADDR_NONE
2N/A#define INADDR_NONE 0xffffffff
2N/A#endif
2N/A
2N/A /*
2N/A * Solaris 2.4 gethostbyname() has problems with multihomed hosts. When
2N/A * doing DNS through NIS, only one host address ends up in the address list.
2N/A * All other addresses end up in the hostname alias list, interspersed with
2N/A * copies of the official host name. This would wreak havoc with tcpd's
2N/A * hostname double checks. Below is a workaround that should do no harm when
2N/A * accidentally left in. A side effect of the workaround is that address
2N/A * list members are no longer properly aligned for structure access.
2N/A */
2N/A
2N/A#ifdef SOLARIS_24_GETHOSTBYNAME_BUG
2N/A
2N/A#undef gethostbyname
2N/A
2N/Astruct hostent *fix_gethostbyname(name)
2N/Achar *name;
2N/A{
2N/A struct hostent *hp;
2N/A struct in_addr addr;
2N/A char **o_addr_list;
2N/A char **o_aliases;
2N/A char **n_addr_list;
2N/A int broken_gethostbyname = 0;
2N/A
2N/A if ((hp = gethostbyname(name)) && !hp->h_addr_list[1] && hp->h_aliases[1]) {
2N/A for (o_aliases = n_addr_list = hp->h_aliases; *o_aliases; o_aliases++) {
2N/A if ((addr.s_addr = inet_addr(*o_aliases)) != INADDR_NONE) {
2N/A memcpy(*n_addr_list++, (char *) &addr, hp->h_length);
2N/A broken_gethostbyname = 1;
2N/A }
2N/A }
2N/A if (broken_gethostbyname) {
2N/A o_addr_list = hp->h_addr_list;
2N/A memcpy(*n_addr_list++, *o_addr_list, hp->h_length);
2N/A *n_addr_list = 0;
2N/A hp->h_addr_list = hp->h_aliases;
2N/A hp->h_aliases = o_addr_list + 1;
2N/A }
2N/A }
2N/A return (hp);
2N/A}
2N/A
2N/A#endif /* SOLARIS_24_GETHOSTBYNAME_BUG */
2N/A
2N/A /*
2N/A * Horror! Some FreeBSD 2.0 libc routines call strtok(). Since tcpd depends
2N/A * heavily on strtok(), strange things may happen. Workaround: use our
2N/A * private strtok(). This has been fixed in the meantime.
2N/A */
2N/A
2N/A#ifdef USE_STRSEP
2N/A
2N/Achar *fix_strtok(buf, sep)
2N/Achar *buf;
2N/Achar *sep;
2N/A{
2N/A static char *state;
2N/A char *result;
2N/A
2N/A if (buf)
2N/A state = buf;
2N/A while ((result = strsep(&state, sep)) && result[0] == 0)
2N/A /* void */ ;
2N/A return (result);
2N/A}
2N/A
2N/A#endif /* USE_STRSEP */
2N/A
2N/A /*
2N/A * IRIX 5.3 (and possibly earlier versions, too) library routines call the
2N/A * non-reentrant strtok() library routine, causing hosts to slip through
2N/A * allow/deny filters. Workaround: don't rely on the vendor and use our own
2N/A * strtok() function. FreeBSD 2.0 has a similar problem (fixed in 2.0.5).
2N/A */
2N/A
2N/A#ifdef LIBC_CALLS_STRTOK
2N/A
2N/Achar *my_strtok(buf, sep)
2N/Achar *buf;
2N/Achar *sep;
2N/A{
2N/A static char *state;
2N/A char *result;
2N/A
2N/A if (buf)
2N/A state = buf;
2N/A
2N/A /*
2N/A * Skip over separator characters and detect end of string.
2N/A */
2N/A if (*(state += strspn(state, sep)) == 0)
2N/A return (0);
2N/A
2N/A /*
2N/A * Skip over non-separator characters and terminate result.
2N/A */
2N/A result = state;
2N/A if (*(state += strcspn(state, sep)) != 0)
2N/A *state++ = 0;
2N/A return (result);
2N/A}
2N/A
2N/A#endif /* LIBC_CALLS_STRTOK */