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