144743Smarkm /* 244743Smarkm * Workarounds for known system software bugs. This module provides wrappers 344743Smarkm * around library functions and system calls that are known to have problems 444743Smarkm * on some systems. Most of these workarounds won't do any harm on regular 544743Smarkm * systems. 644743Smarkm * 744743Smarkm * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 856977Sshin * 956977Sshin * $FreeBSD$ 1044743Smarkm */ 1144743Smarkm 1244743Smarkm#ifndef lint 1344743Smarkmchar sccsid[] = "@(#) workarounds.c 1.6 96/03/19 16:22:25"; 1444743Smarkm#endif 1544743Smarkm 1644743Smarkm#include <sys/types.h> 1744743Smarkm#include <sys/param.h> 1844743Smarkm#include <sys/socket.h> 1944743Smarkm#include <netinet/in.h> 2044743Smarkm#include <arpa/inet.h> 2144743Smarkm#include <netdb.h> 2244743Smarkm#include <errno.h> 2344743Smarkm#include <stdio.h> 2444743Smarkm#include <syslog.h> 2544743Smarkm#include <string.h> 2644743Smarkm 2744743Smarkmextern int errno; 2844743Smarkm 2944743Smarkm#include "tcpd.h" 3044743Smarkm 3144743Smarkm /* 3244743Smarkm * Some AIX versions advertise a too small MAXHOSTNAMELEN value (32). 3344743Smarkm * Result: long hostnames would be truncated, and connections would be 3444743Smarkm * dropped because of host name verification failures. Adrian van Bloois 3544743Smarkm * (A.vanBloois@info.nic.surfnet.nl) figured out what was the problem. 3644743Smarkm */ 3744743Smarkm 3844743Smarkm#if (MAXHOSTNAMELEN < 64) 3944743Smarkm#undef MAXHOSTNAMELEN 4044743Smarkm#endif 4144743Smarkm 4244743Smarkm/* In case not defined in <sys/param.h>. */ 4344743Smarkm 4444743Smarkm#ifndef MAXHOSTNAMELEN 4544743Smarkm#define MAXHOSTNAMELEN 256 /* storage for host name */ 4644743Smarkm#endif 4744743Smarkm 4844743Smarkm /* 4944743Smarkm * Some DG/UX inet_addr() versions return a struct/union instead of a long. 5044743Smarkm * You have this problem when the compiler complains about illegal lvalues 5144743Smarkm * or something like that. The following code fixes this mutant behaviour. 5244743Smarkm * It should not be enabled on "normal" systems. 5344743Smarkm * 5444743Smarkm * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander). 5544743Smarkm */ 5644743Smarkm 5744743Smarkm#ifdef INET_ADDR_BUG 5844743Smarkm 5944743Smarkm#undef inet_addr 6044743Smarkm 6144743Smarkmlong fix_inet_addr(string) 6244743Smarkmchar *string; 6344743Smarkm{ 6444743Smarkm return (inet_addr(string).s_addr); 6544743Smarkm} 6644743Smarkm 6744743Smarkm#endif /* INET_ADDR_BUG */ 6844743Smarkm 6944743Smarkm /* 7044743Smarkm * With some System-V versions, the fgets() library function does not 7144743Smarkm * account for partial reads from e.g. sockets. The result is that fgets() 7244743Smarkm * gives up too soon, causing username lookups to fail. Problem first 7344743Smarkm * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>. 7444743Smarkm * The following code works around the problem. It does no harm on "normal" 7544743Smarkm * systems. 7644743Smarkm */ 7744743Smarkm 7844743Smarkm#ifdef BROKEN_FGETS 7944743Smarkm 8044743Smarkm#undef fgets 8144743Smarkm 8244743Smarkmchar *fix_fgets(buf, len, fp) 8344743Smarkmchar *buf; 8444743Smarkmint len; 8544743SmarkmFILE *fp; 8644743Smarkm{ 8744743Smarkm char *cp = buf; 8844743Smarkm int c; 8944743Smarkm 9044743Smarkm /* 9144743Smarkm * Copy until the buffer fills up, until EOF, or until a newline is 9244743Smarkm * found. 9344743Smarkm */ 9444743Smarkm while (len > 1 && (c = getc(fp)) != EOF) { 9544743Smarkm len--; 9644743Smarkm *cp++ = c; 9744743Smarkm if (c == '\n') 9844743Smarkm break; 9944743Smarkm } 10044743Smarkm 10144743Smarkm /* 10244743Smarkm * Return 0 if nothing was read. This is correct even when a silly buffer 10344743Smarkm * length was specified. 10444743Smarkm */ 10544743Smarkm if (cp > buf) { 10644743Smarkm *cp = 0; 10744743Smarkm return (buf); 10844743Smarkm } else { 10944743Smarkm return (0); 11044743Smarkm } 11144743Smarkm} 11244743Smarkm 11344743Smarkm#endif /* BROKEN_FGETS */ 11444743Smarkm 11544743Smarkm /* 11644743Smarkm * With early SunOS 5 versions, recvfrom() does not completely fill in the 11744743Smarkm * source address structure when doing a non-destructive read. The following 11844743Smarkm * code works around the problem. It does no harm on "normal" systems. 11944743Smarkm */ 12044743Smarkm 12144743Smarkm#ifdef RECVFROM_BUG 12244743Smarkm 12344743Smarkm#undef recvfrom 12444743Smarkm 12544743Smarkmint fix_recvfrom(sock, buf, buflen, flags, from, fromlen) 12644743Smarkmint sock; 12744743Smarkmchar *buf; 12844743Smarkmint buflen; 12944743Smarkmint flags; 13044743Smarkmstruct sockaddr *from; 13144743Smarkmint *fromlen; 13244743Smarkm{ 13344743Smarkm int ret; 13444743Smarkm 13544743Smarkm /* Assume that both ends of a socket belong to the same address family. */ 13644743Smarkm 13744743Smarkm if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) { 13844743Smarkm if (from->sa_family == 0) { 13944743Smarkm struct sockaddr my_addr; 14044743Smarkm int my_addr_len = sizeof(my_addr); 14144743Smarkm 14244743Smarkm if (getsockname(0, &my_addr, &my_addr_len)) { 14344743Smarkm tcpd_warn("getsockname: %m"); 14444743Smarkm } else { 14544743Smarkm from->sa_family = my_addr.sa_family; 14644743Smarkm } 14744743Smarkm } 14844743Smarkm } 14944743Smarkm return (ret); 15044743Smarkm} 15144743Smarkm 15244743Smarkm#endif /* RECVFROM_BUG */ 15344743Smarkm 15444743Smarkm /* 15544743Smarkm * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an 15644743Smarkm * error in case of a datagram-oriented socket. Instead, they claim that all 15744743Smarkm * UDP requests come from address 0.0.0.0. The following code works around 15844743Smarkm * the problem. It does no harm on "normal" systems. 15944743Smarkm */ 16044743Smarkm 16144743Smarkm#ifdef GETPEERNAME_BUG 16244743Smarkm 16344743Smarkm#undef getpeername 16444743Smarkm 16544743Smarkmint fix_getpeername(sock, sa, len) 16644743Smarkmint sock; 16744743Smarkmstruct sockaddr *sa; 16844743Smarkmint *len; 16944743Smarkm{ 17044743Smarkm int ret; 17156977Sshin#ifdef INET6 17256977Sshin struct sockaddr *sin = sa; 17356977Sshin#else 17444743Smarkm struct sockaddr_in *sin = (struct sockaddr_in *) sa; 17556977Sshin#endif 17644743Smarkm 17744743Smarkm if ((ret = getpeername(sock, sa, len)) >= 0 17856977Sshin#ifdef INET6 17956977Sshin && ((sin->su_si.si_family == AF_INET6 18056977Sshin && IN6_IS_ADDR_UNSPECIFIED(&sin->su_sin6.sin6_addr)) 18156977Sshin || (sin->su_si.si_family == AF_INET 18256977Sshin && sin->su_sin.sin_addr.s_addr == 0))) { 18356977Sshin#else 18444743Smarkm && sa->sa_family == AF_INET 18544743Smarkm && sin->sin_addr.s_addr == 0) { 18656977Sshin#endif 18744743Smarkm errno = ENOTCONN; 18844743Smarkm return (-1); 18944743Smarkm } else { 19044743Smarkm return (ret); 19144743Smarkm } 19244743Smarkm} 19344743Smarkm 19444743Smarkm#endif /* GETPEERNAME_BUG */ 19544743Smarkm 19644743Smarkm /* 19744743Smarkm * According to Karl Vogel (vogelke@c-17igp.wpafb.af.mil) some Pyramid 19844743Smarkm * versions have no yp_default_domain() function. We use getdomainname() 19944743Smarkm * instead. 20044743Smarkm */ 20144743Smarkm 20244743Smarkm#ifdef USE_GETDOMAIN 20344743Smarkm 20444743Smarkmint yp_get_default_domain(ptr) 20544743Smarkmchar **ptr; 20644743Smarkm{ 20744743Smarkm static char mydomain[MAXHOSTNAMELEN]; 20844743Smarkm 20944743Smarkm *ptr = mydomain; 21044743Smarkm return (getdomainname(mydomain, MAXHOSTNAMELEN)); 21144743Smarkm} 21244743Smarkm 21344743Smarkm#endif /* USE_GETDOMAIN */ 21444743Smarkm 21544743Smarkm#ifndef INADDR_NONE 21644743Smarkm#define INADDR_NONE 0xffffffff 21744743Smarkm#endif 21844743Smarkm 21944743Smarkm /* 22044743Smarkm * Solaris 2.4 gethostbyname() has problems with multihomed hosts. When 22144743Smarkm * doing DNS through NIS, only one host address ends up in the address list. 22244743Smarkm * All other addresses end up in the hostname alias list, interspersed with 22344743Smarkm * copies of the official host name. This would wreak havoc with tcpd's 22444743Smarkm * hostname double checks. Below is a workaround that should do no harm when 22544743Smarkm * accidentally left in. A side effect of the workaround is that address 22644743Smarkm * list members are no longer properly aligned for structure access. 22744743Smarkm */ 22844743Smarkm 22944743Smarkm#ifdef SOLARIS_24_GETHOSTBYNAME_BUG 23044743Smarkm 23144743Smarkm#undef gethostbyname 23244743Smarkm 23344743Smarkmstruct hostent *fix_gethostbyname(name) 23444743Smarkmchar *name; 23544743Smarkm{ 23644743Smarkm struct hostent *hp; 23744743Smarkm struct in_addr addr; 23844743Smarkm char **o_addr_list; 23944743Smarkm char **o_aliases; 24044743Smarkm char **n_addr_list; 24144743Smarkm int broken_gethostbyname = 0; 24244743Smarkm 24344743Smarkm if ((hp = gethostbyname(name)) && !hp->h_addr_list[1] && hp->h_aliases[1]) { 24444743Smarkm for (o_aliases = n_addr_list = hp->h_aliases; *o_aliases; o_aliases++) { 24544743Smarkm if ((addr.s_addr = inet_addr(*o_aliases)) != INADDR_NONE) { 24644743Smarkm memcpy(*n_addr_list++, (char *) &addr, hp->h_length); 24744743Smarkm broken_gethostbyname = 1; 24844743Smarkm } 24944743Smarkm } 25044743Smarkm if (broken_gethostbyname) { 25144743Smarkm o_addr_list = hp->h_addr_list; 25244743Smarkm memcpy(*n_addr_list++, *o_addr_list, hp->h_length); 25344743Smarkm *n_addr_list = 0; 25444743Smarkm hp->h_addr_list = hp->h_aliases; 25544743Smarkm hp->h_aliases = o_addr_list + 1; 25644743Smarkm } 25744743Smarkm } 25844743Smarkm return (hp); 25944743Smarkm} 26044743Smarkm 26144743Smarkm#endif /* SOLARIS_24_GETHOSTBYNAME_BUG */ 26244743Smarkm 26344743Smarkm /* 26444743Smarkm * Horror! Some FreeBSD 2.0 libc routines call strtok(). Since tcpd depends 26544743Smarkm * heavily on strtok(), strange things may happen. Workaround: use our 26644743Smarkm * private strtok(). This has been fixed in the meantime. 26744743Smarkm */ 26844743Smarkm 26944743Smarkm#ifdef USE_STRSEP 27044743Smarkm 27144743Smarkmchar *fix_strtok(buf, sep) 27244743Smarkmchar *buf; 27344743Smarkmchar *sep; 27444743Smarkm{ 27544743Smarkm static char *state; 27644743Smarkm char *result; 27744743Smarkm 27844743Smarkm if (buf) 27944743Smarkm state = buf; 28044743Smarkm while ((result = strsep(&state, sep)) && result[0] == 0) 28144743Smarkm /* void */ ; 28244743Smarkm return (result); 28344743Smarkm} 28444743Smarkm 28544743Smarkm#endif /* USE_STRSEP */ 28644743Smarkm 28744743Smarkm /* 28844743Smarkm * IRIX 5.3 (and possibly earlier versions, too) library routines call the 28944743Smarkm * non-reentrant strtok() library routine, causing hosts to slip through 29044743Smarkm * allow/deny filters. Workaround: don't rely on the vendor and use our own 29144743Smarkm * strtok() function. FreeBSD 2.0 has a similar problem (fixed in 2.0.5). 29244743Smarkm */ 29344743Smarkm 29444743Smarkm#ifdef LIBC_CALLS_STRTOK 29544743Smarkm 29644743Smarkmchar *my_strtok(buf, sep) 29744743Smarkmchar *buf; 29844743Smarkmchar *sep; 29944743Smarkm{ 30044743Smarkm static char *state; 30144743Smarkm char *result; 30244743Smarkm 30344743Smarkm if (buf) 30444743Smarkm state = buf; 30544743Smarkm 30644743Smarkm /* 30744743Smarkm * Skip over separator characters and detect end of string. 30844743Smarkm */ 30944743Smarkm if (*(state += strspn(state, sep)) == 0) 31044743Smarkm return (0); 31144743Smarkm 31244743Smarkm /* 31344743Smarkm * Skip over non-separator characters and terminate result. 31444743Smarkm */ 31544743Smarkm result = state; 31644743Smarkm if (*(state += strcspn(state, sep)) != 0) 31744743Smarkm *state++ = 0; 31844743Smarkm return (result); 31944743Smarkm} 32044743Smarkm 32144743Smarkm#endif /* LIBC_CALLS_STRTOK */ 322