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