12aef6930SMark Murray /* 22aef6930SMark Murray * Workarounds for known system software bugs. This module provides wrappers 32aef6930SMark Murray * around library functions and system calls that are known to have problems 42aef6930SMark Murray * on some systems. Most of these workarounds won't do any harm on regular 52aef6930SMark Murray * systems. 62aef6930SMark Murray * 72aef6930SMark Murray * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 88053080cSYoshinobu Inoue * 98053080cSYoshinobu Inoue * $FreeBSD$ 102aef6930SMark Murray */ 112aef6930SMark Murray 122aef6930SMark Murray #ifndef lint 132aef6930SMark Murray char sccsid[] = "@(#) workarounds.c 1.6 96/03/19 16:22:25"; 142aef6930SMark Murray #endif 152aef6930SMark Murray 162aef6930SMark Murray #include <sys/types.h> 172aef6930SMark Murray #include <sys/param.h> 182aef6930SMark Murray #include <sys/socket.h> 192aef6930SMark Murray #include <netinet/in.h> 202aef6930SMark Murray #include <arpa/inet.h> 212aef6930SMark Murray #include <netdb.h> 222aef6930SMark Murray #include <errno.h> 232aef6930SMark Murray #include <stdio.h> 242aef6930SMark Murray #include <syslog.h> 252aef6930SMark Murray #include <string.h> 262aef6930SMark Murray 272aef6930SMark Murray extern int errno; 282aef6930SMark Murray 292aef6930SMark Murray #include "tcpd.h" 302aef6930SMark Murray 312aef6930SMark Murray /* 322aef6930SMark Murray * Some AIX versions advertise a too small MAXHOSTNAMELEN value (32). 332aef6930SMark Murray * Result: long hostnames would be truncated, and connections would be 342aef6930SMark Murray * dropped because of host name verification failures. Adrian van Bloois 352aef6930SMark Murray * (A.vanBloois@info.nic.surfnet.nl) figured out what was the problem. 362aef6930SMark Murray */ 372aef6930SMark Murray 382aef6930SMark Murray #if (MAXHOSTNAMELEN < 64) 392aef6930SMark Murray #undef MAXHOSTNAMELEN 402aef6930SMark Murray #endif 412aef6930SMark Murray 422aef6930SMark Murray /* In case not defined in <sys/param.h>. */ 432aef6930SMark Murray 442aef6930SMark Murray #ifndef MAXHOSTNAMELEN 452aef6930SMark Murray #define MAXHOSTNAMELEN 256 /* storage for host name */ 462aef6930SMark Murray #endif 472aef6930SMark Murray 482aef6930SMark Murray /* 492aef6930SMark Murray * Some DG/UX inet_addr() versions return a struct/union instead of a long. 502aef6930SMark Murray * You have this problem when the compiler complains about illegal lvalues 512aef6930SMark Murray * or something like that. The following code fixes this mutant behaviour. 522aef6930SMark Murray * It should not be enabled on "normal" systems. 532aef6930SMark Murray * 542aef6930SMark Murray * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander). 552aef6930SMark Murray */ 562aef6930SMark Murray 572aef6930SMark Murray #ifdef INET_ADDR_BUG 582aef6930SMark Murray 592aef6930SMark Murray #undef inet_addr 602aef6930SMark Murray 612aef6930SMark Murray long fix_inet_addr(string) 622aef6930SMark Murray char *string; 632aef6930SMark Murray { 642aef6930SMark Murray return (inet_addr(string).s_addr); 652aef6930SMark Murray } 662aef6930SMark Murray 672aef6930SMark Murray #endif /* INET_ADDR_BUG */ 682aef6930SMark Murray 692aef6930SMark Murray /* 702aef6930SMark Murray * With some System-V versions, the fgets() library function does not 712aef6930SMark Murray * account for partial reads from e.g. sockets. The result is that fgets() 722aef6930SMark Murray * gives up too soon, causing username lookups to fail. Problem first 732aef6930SMark Murray * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>. 742aef6930SMark Murray * The following code works around the problem. It does no harm on "normal" 752aef6930SMark Murray * systems. 762aef6930SMark Murray */ 772aef6930SMark Murray 782aef6930SMark Murray #ifdef BROKEN_FGETS 792aef6930SMark Murray 802aef6930SMark Murray #undef fgets 812aef6930SMark Murray 822aef6930SMark Murray char *fix_fgets(buf, len, fp) 832aef6930SMark Murray char *buf; 842aef6930SMark Murray int len; 852aef6930SMark Murray FILE *fp; 862aef6930SMark Murray { 872aef6930SMark Murray char *cp = buf; 882aef6930SMark Murray int c; 892aef6930SMark Murray 902aef6930SMark Murray /* 912aef6930SMark Murray * Copy until the buffer fills up, until EOF, or until a newline is 922aef6930SMark Murray * found. 932aef6930SMark Murray */ 942aef6930SMark Murray while (len > 1 && (c = getc(fp)) != EOF) { 952aef6930SMark Murray len--; 962aef6930SMark Murray *cp++ = c; 972aef6930SMark Murray if (c == '\n') 982aef6930SMark Murray break; 992aef6930SMark Murray } 1002aef6930SMark Murray 1012aef6930SMark Murray /* 1022aef6930SMark Murray * Return 0 if nothing was read. This is correct even when a silly buffer 1032aef6930SMark Murray * length was specified. 1042aef6930SMark Murray */ 1052aef6930SMark Murray if (cp > buf) { 1062aef6930SMark Murray *cp = 0; 1072aef6930SMark Murray return (buf); 1082aef6930SMark Murray } else { 1092aef6930SMark Murray return (0); 1102aef6930SMark Murray } 1112aef6930SMark Murray } 1122aef6930SMark Murray 1132aef6930SMark Murray #endif /* BROKEN_FGETS */ 1142aef6930SMark Murray 1152aef6930SMark Murray /* 1162aef6930SMark Murray * With early SunOS 5 versions, recvfrom() does not completely fill in the 1172aef6930SMark Murray * source address structure when doing a non-destructive read. The following 1182aef6930SMark Murray * code works around the problem. It does no harm on "normal" systems. 1192aef6930SMark Murray */ 1202aef6930SMark Murray 1212aef6930SMark Murray #ifdef RECVFROM_BUG 1222aef6930SMark Murray 1232aef6930SMark Murray #undef recvfrom 1242aef6930SMark Murray 1252aef6930SMark Murray int fix_recvfrom(sock, buf, buflen, flags, from, fromlen) 1262aef6930SMark Murray int sock; 1272aef6930SMark Murray char *buf; 1282aef6930SMark Murray int buflen; 1292aef6930SMark Murray int flags; 1302aef6930SMark Murray struct sockaddr *from; 1312aef6930SMark Murray int *fromlen; 1322aef6930SMark Murray { 1332aef6930SMark Murray int ret; 1342aef6930SMark Murray 1352aef6930SMark Murray /* Assume that both ends of a socket belong to the same address family. */ 1362aef6930SMark Murray 1372aef6930SMark Murray if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) { 1382aef6930SMark Murray if (from->sa_family == 0) { 1392aef6930SMark Murray struct sockaddr my_addr; 1402aef6930SMark Murray int my_addr_len = sizeof(my_addr); 1412aef6930SMark Murray 1422aef6930SMark Murray if (getsockname(0, &my_addr, &my_addr_len)) { 1432aef6930SMark Murray tcpd_warn("getsockname: %m"); 1442aef6930SMark Murray } else { 1452aef6930SMark Murray from->sa_family = my_addr.sa_family; 1462aef6930SMark Murray } 1472aef6930SMark Murray } 1482aef6930SMark Murray } 1492aef6930SMark Murray return (ret); 1502aef6930SMark Murray } 1512aef6930SMark Murray 1522aef6930SMark Murray #endif /* RECVFROM_BUG */ 1532aef6930SMark Murray 1542aef6930SMark Murray /* 1552aef6930SMark Murray * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an 1562aef6930SMark Murray * error in case of a datagram-oriented socket. Instead, they claim that all 1572aef6930SMark Murray * UDP requests come from address 0.0.0.0. The following code works around 1582aef6930SMark Murray * the problem. It does no harm on "normal" systems. 1592aef6930SMark Murray */ 1602aef6930SMark Murray 1612aef6930SMark Murray #ifdef GETPEERNAME_BUG 1622aef6930SMark Murray 1632aef6930SMark Murray #undef getpeername 1642aef6930SMark Murray 1652aef6930SMark Murray int fix_getpeername(sock, sa, len) 1662aef6930SMark Murray int sock; 1672aef6930SMark Murray struct sockaddr *sa; 1682aef6930SMark Murray int *len; 1692aef6930SMark Murray { 1702aef6930SMark Murray int ret; 1718053080cSYoshinobu Inoue #ifdef INET6 1728053080cSYoshinobu Inoue struct sockaddr *sin = sa; 1738053080cSYoshinobu Inoue #else 1742aef6930SMark Murray struct sockaddr_in *sin = (struct sockaddr_in *) sa; 1758053080cSYoshinobu Inoue #endif 1762aef6930SMark Murray 1772aef6930SMark Murray if ((ret = getpeername(sock, sa, len)) >= 0 1788053080cSYoshinobu Inoue #ifdef INET6 1798053080cSYoshinobu Inoue && ((sin->su_si.si_family == AF_INET6 1808053080cSYoshinobu Inoue && IN6_IS_ADDR_UNSPECIFIED(&sin->su_sin6.sin6_addr)) 1818053080cSYoshinobu Inoue || (sin->su_si.si_family == AF_INET 1828053080cSYoshinobu Inoue && sin->su_sin.sin_addr.s_addr == 0))) { 1838053080cSYoshinobu Inoue #else 1842aef6930SMark Murray && sa->sa_family == AF_INET 1852aef6930SMark Murray && sin->sin_addr.s_addr == 0) { 1868053080cSYoshinobu Inoue #endif 1872aef6930SMark Murray errno = ENOTCONN; 1882aef6930SMark Murray return (-1); 1892aef6930SMark Murray } else { 1902aef6930SMark Murray return (ret); 1912aef6930SMark Murray } 1922aef6930SMark Murray } 1932aef6930SMark Murray 1942aef6930SMark Murray #endif /* GETPEERNAME_BUG */ 1952aef6930SMark Murray 1962aef6930SMark Murray /* 1972aef6930SMark Murray * According to Karl Vogel (vogelke@c-17igp.wpafb.af.mil) some Pyramid 1982aef6930SMark Murray * versions have no yp_default_domain() function. We use getdomainname() 1992aef6930SMark Murray * instead. 2002aef6930SMark Murray */ 2012aef6930SMark Murray 2022aef6930SMark Murray #ifdef USE_GETDOMAIN 2032aef6930SMark Murray 2042aef6930SMark Murray int yp_get_default_domain(ptr) 2052aef6930SMark Murray char **ptr; 2062aef6930SMark Murray { 2072aef6930SMark Murray static char mydomain[MAXHOSTNAMELEN]; 2082aef6930SMark Murray 2092aef6930SMark Murray *ptr = mydomain; 2102aef6930SMark Murray return (getdomainname(mydomain, MAXHOSTNAMELEN)); 2112aef6930SMark Murray } 2122aef6930SMark Murray 2132aef6930SMark Murray #endif /* USE_GETDOMAIN */ 2142aef6930SMark Murray 2152aef6930SMark Murray #ifndef INADDR_NONE 2162aef6930SMark Murray #define INADDR_NONE 0xffffffff 2172aef6930SMark Murray #endif 2182aef6930SMark Murray 2192aef6930SMark Murray /* 2202aef6930SMark Murray * Solaris 2.4 gethostbyname() has problems with multihomed hosts. When 2212aef6930SMark Murray * doing DNS through NIS, only one host address ends up in the address list. 2222aef6930SMark Murray * All other addresses end up in the hostname alias list, interspersed with 2232aef6930SMark Murray * copies of the official host name. This would wreak havoc with tcpd's 2242aef6930SMark Murray * hostname double checks. Below is a workaround that should do no harm when 2252aef6930SMark Murray * accidentally left in. A side effect of the workaround is that address 2262aef6930SMark Murray * list members are no longer properly aligned for structure access. 2272aef6930SMark Murray */ 2282aef6930SMark Murray 2292aef6930SMark Murray #ifdef SOLARIS_24_GETHOSTBYNAME_BUG 2302aef6930SMark Murray 2312aef6930SMark Murray #undef gethostbyname 2322aef6930SMark Murray 2332aef6930SMark Murray struct hostent *fix_gethostbyname(name) 2342aef6930SMark Murray char *name; 2352aef6930SMark Murray { 2362aef6930SMark Murray struct hostent *hp; 2372aef6930SMark Murray struct in_addr addr; 2382aef6930SMark Murray char **o_addr_list; 2392aef6930SMark Murray char **o_aliases; 2402aef6930SMark Murray char **n_addr_list; 2412aef6930SMark Murray int broken_gethostbyname = 0; 2422aef6930SMark Murray 2432aef6930SMark Murray if ((hp = gethostbyname(name)) && !hp->h_addr_list[1] && hp->h_aliases[1]) { 2442aef6930SMark Murray for (o_aliases = n_addr_list = hp->h_aliases; *o_aliases; o_aliases++) { 2452aef6930SMark Murray if ((addr.s_addr = inet_addr(*o_aliases)) != INADDR_NONE) { 2462aef6930SMark Murray memcpy(*n_addr_list++, (char *) &addr, hp->h_length); 2472aef6930SMark Murray broken_gethostbyname = 1; 2482aef6930SMark Murray } 2492aef6930SMark Murray } 2502aef6930SMark Murray if (broken_gethostbyname) { 2512aef6930SMark Murray o_addr_list = hp->h_addr_list; 2522aef6930SMark Murray memcpy(*n_addr_list++, *o_addr_list, hp->h_length); 2532aef6930SMark Murray *n_addr_list = 0; 2542aef6930SMark Murray hp->h_addr_list = hp->h_aliases; 2552aef6930SMark Murray hp->h_aliases = o_addr_list + 1; 2562aef6930SMark Murray } 2572aef6930SMark Murray } 2582aef6930SMark Murray return (hp); 2592aef6930SMark Murray } 2602aef6930SMark Murray 2612aef6930SMark Murray #endif /* SOLARIS_24_GETHOSTBYNAME_BUG */ 2622aef6930SMark Murray 2632aef6930SMark Murray /* 2642aef6930SMark Murray * Horror! Some FreeBSD 2.0 libc routines call strtok(). Since tcpd depends 2652aef6930SMark Murray * heavily on strtok(), strange things may happen. Workaround: use our 2662aef6930SMark Murray * private strtok(). This has been fixed in the meantime. 2672aef6930SMark Murray */ 2682aef6930SMark Murray 2692aef6930SMark Murray #ifdef USE_STRSEP 2702aef6930SMark Murray 2712aef6930SMark Murray char *fix_strtok(buf, sep) 2722aef6930SMark Murray char *buf; 2732aef6930SMark Murray char *sep; 2742aef6930SMark Murray { 2752aef6930SMark Murray static char *state; 2762aef6930SMark Murray char *result; 2772aef6930SMark Murray 2782aef6930SMark Murray if (buf) 2792aef6930SMark Murray state = buf; 2802aef6930SMark Murray while ((result = strsep(&state, sep)) && result[0] == 0) 2812aef6930SMark Murray /* void */ ; 2822aef6930SMark Murray return (result); 2832aef6930SMark Murray } 2842aef6930SMark Murray 2852aef6930SMark Murray #endif /* USE_STRSEP */ 2862aef6930SMark Murray 2872aef6930SMark Murray /* 2882aef6930SMark Murray * IRIX 5.3 (and possibly earlier versions, too) library routines call the 2892aef6930SMark Murray * non-reentrant strtok() library routine, causing hosts to slip through 2902aef6930SMark Murray * allow/deny filters. Workaround: don't rely on the vendor and use our own 2912aef6930SMark Murray * strtok() function. FreeBSD 2.0 has a similar problem (fixed in 2.0.5). 2922aef6930SMark Murray */ 2932aef6930SMark Murray 2942aef6930SMark Murray #ifdef LIBC_CALLS_STRTOK 2952aef6930SMark Murray 2962aef6930SMark Murray char *my_strtok(buf, sep) 2972aef6930SMark Murray char *buf; 2982aef6930SMark Murray char *sep; 2992aef6930SMark Murray { 3002aef6930SMark Murray static char *state; 3012aef6930SMark Murray char *result; 3022aef6930SMark Murray 3032aef6930SMark Murray if (buf) 3042aef6930SMark Murray state = buf; 3052aef6930SMark Murray 3062aef6930SMark Murray /* 3072aef6930SMark Murray * Skip over separator characters and detect end of string. 3082aef6930SMark Murray */ 3092aef6930SMark Murray if (*(state += strspn(state, sep)) == 0) 3102aef6930SMark Murray return (0); 3112aef6930SMark Murray 3122aef6930SMark Murray /* 3132aef6930SMark Murray * Skip over non-separator characters and terminate result. 3142aef6930SMark Murray */ 3152aef6930SMark Murray result = state; 3162aef6930SMark Murray if (*(state += strcspn(state, sep)) != 0) 3172aef6930SMark Murray *state++ = 0; 3182aef6930SMark Murray return (result); 3192aef6930SMark Murray } 3202aef6930SMark Murray 3212aef6930SMark Murray #endif /* LIBC_CALLS_STRTOK */ 322