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 11 char 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 25 extern 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 59 long fix_inet_addr(string) 60 char *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 80 char *fix_fgets(buf, len, fp) 81 char *buf; 82 int len; 83 FILE *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 123 int fix_recvfrom(sock, buf, buflen, flags, from, fromlen) 124 int sock; 125 char *buf; 126 int buflen; 127 int flags; 128 struct sockaddr *from; 129 int *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 163 int fix_getpeername(sock, sa, len) 164 int sock; 165 struct sockaddr *sa; 166 int *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 191 int yp_get_default_domain(ptr) 192 char **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 220 struct hostent *fix_gethostbyname(name) 221 char *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 258 char *fix_strtok(buf, sep) 259 char *buf; 260 char *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 283 char *my_strtok(buf, sep) 284 char *buf; 285 char *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