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