workarounds.c revision 44743
1 /* 2 * Workarounds for known system software bugs. This module provides wrappers 3 * around library functions and system calls that are known to have problems 4 * on some systems. Most of these workarounds won't do any harm on regular 5 * systems. 6 * 7 * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 8 */ 9 10#ifndef lint 11char sccsid[] = "@(#) workarounds.c 1.6 96/03/19 16:22:25"; 12#endif 13 14#include <sys/types.h> 15#include <sys/param.h> 16#include <sys/socket.h> 17#include <netinet/in.h> 18#include <arpa/inet.h> 19#include <netdb.h> 20#include <errno.h> 21#include <stdio.h> 22#include <syslog.h> 23#include <string.h> 24 25extern int errno; 26 27#include "tcpd.h" 28 29 /* 30 * Some AIX versions advertise a too small MAXHOSTNAMELEN value (32). 31 * Result: long hostnames would be truncated, and connections would be 32 * dropped because of host name verification failures. Adrian van Bloois 33 * (A.vanBloois@info.nic.surfnet.nl) figured out what was the problem. 34 */ 35 36#if (MAXHOSTNAMELEN < 64) 37#undef MAXHOSTNAMELEN 38#endif 39 40/* In case not defined in <sys/param.h>. */ 41 42#ifndef MAXHOSTNAMELEN 43#define MAXHOSTNAMELEN 256 /* storage for host name */ 44#endif 45 46 /* 47 * Some DG/UX inet_addr() versions return a struct/union instead of a long. 48 * You have this problem when the compiler complains about illegal lvalues 49 * or something like that. The following code fixes this mutant behaviour. 50 * It should not be enabled on "normal" systems. 51 * 52 * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander). 53 */ 54 55#ifdef INET_ADDR_BUG 56 57#undef inet_addr 58 59long fix_inet_addr(string) 60char *string; 61{ 62 return (inet_addr(string).s_addr); 63} 64 65#endif /* INET_ADDR_BUG */ 66 67 /* 68 * With some System-V versions, the fgets() library function does not 69 * account for partial reads from e.g. sockets. The result is that fgets() 70 * gives up too soon, causing username lookups to fail. Problem first 71 * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>. 72 * The following code works around the problem. It does no harm on "normal" 73 * systems. 74 */ 75 76#ifdef BROKEN_FGETS 77 78#undef fgets 79 80char *fix_fgets(buf, len, fp) 81char *buf; 82int len; 83FILE *fp; 84{ 85 char *cp = buf; 86 int c; 87 88 /* 89 * Copy until the buffer fills up, until EOF, or until a newline is 90 * found. 91 */ 92 while (len > 1 && (c = getc(fp)) != EOF) { 93 len--; 94 *cp++ = c; 95 if (c == '\n') 96 break; 97 } 98 99 /* 100 * Return 0 if nothing was read. This is correct even when a silly buffer 101 * length was specified. 102 */ 103 if (cp > buf) { 104 *cp = 0; 105 return (buf); 106 } else { 107 return (0); 108 } 109} 110 111#endif /* BROKEN_FGETS */ 112 113 /* 114 * With early SunOS 5 versions, recvfrom() does not completely fill in the 115 * source address structure when doing a non-destructive read. The following 116 * code works around the problem. It does no harm on "normal" systems. 117 */ 118 119#ifdef RECVFROM_BUG 120 121#undef recvfrom 122 123int fix_recvfrom(sock, buf, buflen, flags, from, fromlen) 124int sock; 125char *buf; 126int buflen; 127int flags; 128struct sockaddr *from; 129int *fromlen; 130{ 131 int ret; 132 133 /* Assume that both ends of a socket belong to the same address family. */ 134 135 if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) { 136 if (from->sa_family == 0) { 137 struct sockaddr my_addr; 138 int my_addr_len = sizeof(my_addr); 139 140 if (getsockname(0, &my_addr, &my_addr_len)) { 141 tcpd_warn("getsockname: %m"); 142 } else { 143 from->sa_family = my_addr.sa_family; 144 } 145 } 146 } 147 return (ret); 148} 149 150#endif /* RECVFROM_BUG */ 151 152 /* 153 * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an 154 * error in case of a datagram-oriented socket. Instead, they claim that all 155 * UDP requests come from address 0.0.0.0. The following code works around 156 * the problem. It does no harm on "normal" systems. 157 */ 158 159#ifdef GETPEERNAME_BUG 160 161#undef getpeername 162 163int fix_getpeername(sock, sa, len) 164int sock; 165struct sockaddr *sa; 166int *len; 167{ 168 int ret; 169 struct sockaddr_in *sin = (struct sockaddr_in *) sa; 170 171 if ((ret = getpeername(sock, sa, len)) >= 0 172 && sa->sa_family == AF_INET 173 && sin->sin_addr.s_addr == 0) { 174 errno = ENOTCONN; 175 return (-1); 176 } else { 177 return (ret); 178 } 179} 180 181#endif /* GETPEERNAME_BUG */ 182 183 /* 184 * According to Karl Vogel (vogelke@c-17igp.wpafb.af.mil) some Pyramid 185 * versions have no yp_default_domain() function. We use getdomainname() 186 * instead. 187 */ 188 189#ifdef USE_GETDOMAIN 190 191int yp_get_default_domain(ptr) 192char **ptr; 193{ 194 static char mydomain[MAXHOSTNAMELEN]; 195 196 *ptr = mydomain; 197 return (getdomainname(mydomain, MAXHOSTNAMELEN)); 198} 199 200#endif /* USE_GETDOMAIN */ 201 202#ifndef INADDR_NONE 203#define INADDR_NONE 0xffffffff 204#endif 205 206 /* 207 * Solaris 2.4 gethostbyname() has problems with multihomed hosts. When 208 * doing DNS through NIS, only one host address ends up in the address list. 209 * All other addresses end up in the hostname alias list, interspersed with 210 * copies of the official host name. This would wreak havoc with tcpd's 211 * hostname double checks. Below is a workaround that should do no harm when 212 * accidentally left in. A side effect of the workaround is that address 213 * list members are no longer properly aligned for structure access. 214 */ 215 216#ifdef SOLARIS_24_GETHOSTBYNAME_BUG 217 218#undef gethostbyname 219 220struct hostent *fix_gethostbyname(name) 221char *name; 222{ 223 struct hostent *hp; 224 struct in_addr addr; 225 char **o_addr_list; 226 char **o_aliases; 227 char **n_addr_list; 228 int broken_gethostbyname = 0; 229 230 if ((hp = gethostbyname(name)) && !hp->h_addr_list[1] && hp->h_aliases[1]) { 231 for (o_aliases = n_addr_list = hp->h_aliases; *o_aliases; o_aliases++) { 232 if ((addr.s_addr = inet_addr(*o_aliases)) != INADDR_NONE) { 233 memcpy(*n_addr_list++, (char *) &addr, hp->h_length); 234 broken_gethostbyname = 1; 235 } 236 } 237 if (broken_gethostbyname) { 238 o_addr_list = hp->h_addr_list; 239 memcpy(*n_addr_list++, *o_addr_list, hp->h_length); 240 *n_addr_list = 0; 241 hp->h_addr_list = hp->h_aliases; 242 hp->h_aliases = o_addr_list + 1; 243 } 244 } 245 return (hp); 246} 247 248#endif /* SOLARIS_24_GETHOSTBYNAME_BUG */ 249 250 /* 251 * Horror! Some FreeBSD 2.0 libc routines call strtok(). Since tcpd depends 252 * heavily on strtok(), strange things may happen. Workaround: use our 253 * private strtok(). This has been fixed in the meantime. 254 */ 255 256#ifdef USE_STRSEP 257 258char *fix_strtok(buf, sep) 259char *buf; 260char *sep; 261{ 262 static char *state; 263 char *result; 264 265 if (buf) 266 state = buf; 267 while ((result = strsep(&state, sep)) && result[0] == 0) 268 /* void */ ; 269 return (result); 270} 271 272#endif /* USE_STRSEP */ 273 274 /* 275 * IRIX 5.3 (and possibly earlier versions, too) library routines call the 276 * non-reentrant strtok() library routine, causing hosts to slip through 277 * allow/deny filters. Workaround: don't rely on the vendor and use our own 278 * strtok() function. FreeBSD 2.0 has a similar problem (fixed in 2.0.5). 279 */ 280 281#ifdef LIBC_CALLS_STRTOK 282 283char *my_strtok(buf, sep) 284char *buf; 285char *sep; 286{ 287 static char *state; 288 char *result; 289 290 if (buf) 291 state = buf; 292 293 /* 294 * Skip over separator characters and detect end of string. 295 */ 296 if (*(state += strspn(state, sep)) == 0) 297 return (0); 298 299 /* 300 * Skip over non-separator characters and terminate result. 301 */ 302 result = state; 303 if (*(state += strcspn(state, sep)) != 0) 304 *state++ = 0; 305 return (result); 306} 307 308#endif /* LIBC_CALLS_STRTOK */ 309