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