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(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(char *buf, int len, FILE *fp) 83 { 84 char *cp = buf; 85 int c; 86 87 /* 88 * Copy until the buffer fills up, until EOF, or until a newline is 89 * found. 90 */ 91 while (len > 1 && (c = getc(fp)) != EOF) { 92 len--; 93 *cp++ = c; 94 if (c == '\n') 95 break; 96 } 97 98 /* 99 * Return 0 if nothing was read. This is correct even when a silly buffer 100 * length was specified. 101 */ 102 if (cp > buf) { 103 *cp = 0; 104 return (buf); 105 } else { 106 return (0); 107 } 108 } 109 110 #endif /* BROKEN_FGETS */ 111 112 /* 113 * With early SunOS 5 versions, recvfrom() does not completely fill in the 114 * source address structure when doing a non-destructive read. The following 115 * code works around the problem. It does no harm on "normal" systems. 116 */ 117 118 #ifdef RECVFROM_BUG 119 120 #undef recvfrom 121 122 int fix_recvfrom(int sock, char *buf, int buflen, int flags, 123 struct sockaddr *from, int *fromlen) 124 { 125 int ret; 126 127 /* Assume that both ends of a socket belong to the same address family. */ 128 129 if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) { 130 if (from->sa_family == 0) { 131 struct sockaddr my_addr; 132 int my_addr_len = sizeof(my_addr); 133 134 if (getsockname(0, &my_addr, &my_addr_len)) { 135 tcpd_warn("getsockname: %m"); 136 } else { 137 from->sa_family = my_addr.sa_family; 138 } 139 } 140 } 141 return (ret); 142 } 143 144 #endif /* RECVFROM_BUG */ 145 146 /* 147 * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an 148 * error in case of a datagram-oriented socket. Instead, they claim that all 149 * UDP requests come from address 0.0.0.0. The following code works around 150 * the problem. It does no harm on "normal" systems. 151 */ 152 153 #ifdef GETPEERNAME_BUG 154 155 #undef getpeername 156 157 int fix_getpeername(int sock, struct sockaddr *sa, int *len) 158 { 159 int ret; 160 #ifdef INET6 161 struct sockaddr *sin = sa; 162 #else 163 struct sockaddr_in *sin = (struct sockaddr_in *) sa; 164 #endif 165 166 if ((ret = getpeername(sock, sa, len)) >= 0 167 #ifdef INET6 168 && ((sin->su_si.si_family == AF_INET6 169 && IN6_IS_ADDR_UNSPECIFIED(&sin->su_sin6.sin6_addr)) 170 || (sin->su_si.si_family == AF_INET 171 && sin->su_sin.sin_addr.s_addr == 0))) { 172 #else 173 && sa->sa_family == AF_INET 174 && sin->sin_addr.s_addr == 0) { 175 #endif 176 errno = ENOTCONN; 177 return (-1); 178 } else { 179 return (ret); 180 } 181 } 182 183 #endif /* GETPEERNAME_BUG */ 184 185 /* 186 * According to Karl Vogel (vogelke@c-17igp.wpafb.af.mil) some Pyramid 187 * versions have no yp_default_domain() function. We use getdomainname() 188 * instead. 189 */ 190 191 #ifdef USE_GETDOMAIN 192 193 int yp_get_default_domain(char **ptr) 194 { 195 static char mydomain[MAXHOSTNAMELEN]; 196 197 *ptr = mydomain; 198 return (getdomainname(mydomain, MAXHOSTNAMELEN)); 199 } 200 201 #endif /* USE_GETDOMAIN */ 202 203 #ifndef INADDR_NONE 204 #define INADDR_NONE 0xffffffff 205 #endif 206 207 /* 208 * Solaris 2.4 gethostbyname() has problems with multihomed hosts. When 209 * doing DNS through NIS, only one host address ends up in the address list. 210 * All other addresses end up in the hostname alias list, interspersed with 211 * copies of the official host name. This would wreak havoc with tcpd's 212 * hostname double checks. Below is a workaround that should do no harm when 213 * accidentally left in. A side effect of the workaround is that address 214 * list members are no longer properly aligned for structure access. 215 */ 216 217 #ifdef SOLARIS_24_GETHOSTBYNAME_BUG 218 219 #undef gethostbyname 220 221 struct hostent *fix_gethostbyname(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(char *buf, char *sep) 259 { 260 static char *state; 261 char *result; 262 263 if (buf) 264 state = buf; 265 while ((result = strsep(&state, sep)) && result[0] == 0) 266 /* void */ ; 267 return (result); 268 } 269 270 #endif /* USE_STRSEP */ 271 272 /* 273 * IRIX 5.3 (and possibly earlier versions, too) library routines call the 274 * non-reentrant strtok() library routine, causing hosts to slip through 275 * allow/deny filters. Workaround: don't rely on the vendor and use our own 276 * strtok() function. FreeBSD 2.0 has a similar problem (fixed in 2.0.5). 277 */ 278 279 #ifdef LIBC_CALLS_STRTOK 280 281 char *my_strtok(char *buf, char *sep) 282 { 283 static char *state; 284 char *result; 285 286 if (buf) 287 state = buf; 288 289 /* 290 * Skip over separator characters and detect end of string. 291 */ 292 if (*(state += strspn(state, sep)) == 0) 293 return (0); 294 295 /* 296 * Skip over non-separator characters and terminate result. 297 */ 298 result = state; 299 if (*(state += strcspn(state, sep)) != 0) 300 *state++ = 0; 301 return (result); 302 } 303 304 #endif /* LIBC_CALLS_STRTOK */ 305