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