1226748Sjhb /* 2283927Sjhb * Workarounds for known system software bugs. This module provides wrappers 3226748Sjhb * around library functions and system calls that are known to have problems 4226748Sjhb * on some systems. Most of these workarounds won't do any harm on regular 5226748Sjhb * systems. 6226748Sjhb * 7226748Sjhb * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 8226748Sjhb * 9226748Sjhb * $FreeBSD$ 10226748Sjhb */ 11226748Sjhb 12226748Sjhb#ifndef lint 13226748Sjhbchar sccsid[] = "@(#) workarounds.c 1.6 96/03/19 16:22:25"; 14226748Sjhb#endif 15226748Sjhb 16226748Sjhb#include <sys/types.h> 17226748Sjhb#include <sys/param.h> 18226748Sjhb#include <sys/socket.h> 19226748Sjhb#include <netinet/in.h> 20226748Sjhb#include <arpa/inet.h> 21226748Sjhb#include <netdb.h> 22226748Sjhb#include <errno.h> 23226748Sjhb#include <stdio.h> 24226748Sjhb#include <syslog.h> 25226748Sjhb#include <string.h> 26226748Sjhb#ifdef USE_GETDOMAIN 27226748Sjhb#include <unistd.h> 28226748Sjhb#endif 29226748Sjhb 30226748Sjhb#include "tcpd.h" 31226748Sjhb 32226748Sjhb /* 33226748Sjhb * Some AIX versions advertise a too small MAXHOSTNAMELEN value (32). 34226748Sjhb * Result: long hostnames would be truncated, and connections would be 35226748Sjhb * dropped because of host name verification failures. Adrian van Bloois 36226748Sjhb * (A.vanBloois@info.nic.surfnet.nl) figured out what was the problem. 37226748Sjhb */ 38226748Sjhb 39226748Sjhb#if (MAXHOSTNAMELEN < 64) 40226748Sjhb#undef MAXHOSTNAMELEN 41226748Sjhb#endif 42226748Sjhb 43226748Sjhb/* In case not defined in <sys/param.h>. */ 44226748Sjhb 45226748Sjhb#ifndef MAXHOSTNAMELEN 46226748Sjhb#define MAXHOSTNAMELEN 256 /* storage for host name */ 47226748Sjhb#endif 48226748Sjhb 49226748Sjhb /* 50226748Sjhb * Some DG/UX inet_addr() versions return a struct/union instead of a long. 51226748Sjhb * You have this problem when the compiler complains about illegal lvalues 52226748Sjhb * or something like that. The following code fixes this mutant behaviour. 53226748Sjhb * It should not be enabled on "normal" systems. 54226748Sjhb * 55226748Sjhb * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander). 56226748Sjhb */ 57226748Sjhb 58226748Sjhb#ifdef INET_ADDR_BUG 59226748Sjhb 60226748Sjhb#undef inet_addr 61226748Sjhb 62226748Sjhblong fix_inet_addr(char *string) 63226748Sjhb{ 64226748Sjhb return (inet_addr(string).s_addr); 65243025Savg} 66226748Sjhb 67226748Sjhb#endif /* INET_ADDR_BUG */ 68226748Sjhb 69226748Sjhb /* 70226748Sjhb * With some System-V versions, the fgets() library function does not 71226748Sjhb * account for partial reads from e.g. sockets. The result is that fgets() 72226748Sjhb * gives up too soon, causing username lookups to fail. Problem first 73226748Sjhb * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>. 74226748Sjhb * The following code works around the problem. It does no harm on "normal" 75226748Sjhb * systems. 76226748Sjhb */ 77243025Savg 78226748Sjhb#ifdef BROKEN_FGETS 79226748Sjhb 80226748Sjhb#undef fgets 81226748Sjhb 82243025Savgchar *fix_fgets(char *buf, int len, FILE *fp) 83226748Sjhb{ 84226748Sjhb char *cp = buf; 85226748Sjhb int c; 86226748Sjhb 87226748Sjhb /* 88226748Sjhb * Copy until the buffer fills up, until EOF, or until a newline is 89226748Sjhb * found. 90226748Sjhb */ 91226748Sjhb while (len > 1 && (c = getc(fp)) != EOF) { 92226748Sjhb len--; 93226748Sjhb *cp++ = c; 94243025Savg if (c == '\n') 95226748Sjhb break; 96226748Sjhb } 97226748Sjhb 98226748Sjhb /* 99243025Savg * Return 0 if nothing was read. This is correct even when a silly buffer 100226748Sjhb * length was specified. 101226748Sjhb */ 102226748Sjhb if (cp > buf) { 103226748Sjhb *cp = 0; 104226748Sjhb return (buf); 105226748Sjhb } else { 106226748Sjhb return (0); 107226748Sjhb } 108226748Sjhb} 109226748Sjhb 110226748Sjhb#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 122int 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 157int 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 193int 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 221struct 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 258char *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 281char *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