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