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