xref: /freebsd/contrib/tcp_wrappers/workarounds.c (revision 6e8394b8baa7d5d9153ab90de6824bcd19b3b4e1)
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 
10 #ifndef lint
11 char    sccsid[] = "@(#) workarounds.c 1.6 96/03/19 16:22:25";
12 #endif
13 
14 #include <sys/types.h>
15 #include <sys/param.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <netdb.h>
20 #include <errno.h>
21 #include <stdio.h>
22 #include <syslog.h>
23 #include <string.h>
24 
25 extern int errno;
26 
27 #include "tcpd.h"
28 
29  /*
30   * Some AIX versions advertise a too small MAXHOSTNAMELEN value (32).
31   * Result: long hostnames would be truncated, and connections would be
32   * dropped because of host name verification failures. Adrian van Bloois
33   * (A.vanBloois@info.nic.surfnet.nl) figured out what was the problem.
34   */
35 
36 #if (MAXHOSTNAMELEN < 64)
37 #undef MAXHOSTNAMELEN
38 #endif
39 
40 /* In case not defined in <sys/param.h>. */
41 
42 #ifndef MAXHOSTNAMELEN
43 #define MAXHOSTNAMELEN  256             /* storage for host name */
44 #endif
45 
46  /*
47   * Some DG/UX inet_addr() versions return a struct/union instead of a long.
48   * You have this problem when the compiler complains about illegal lvalues
49   * or something like that. The following code fixes this mutant behaviour.
50   * It should not be enabled on "normal" systems.
51   *
52   * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander).
53   */
54 
55 #ifdef INET_ADDR_BUG
56 
57 #undef inet_addr
58 
59 long    fix_inet_addr(string)
60 char   *string;
61 {
62     return (inet_addr(string).s_addr);
63 }
64 
65 #endif /* INET_ADDR_BUG */
66 
67  /*
68   * With some System-V versions, the fgets() library function does not
69   * account for partial reads from e.g. sockets. The result is that fgets()
70   * gives up too soon, causing username lookups to fail. Problem first
71   * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>.
72   * The following code works around the problem. It does no harm on "normal"
73   * systems.
74   */
75 
76 #ifdef BROKEN_FGETS
77 
78 #undef fgets
79 
80 char   *fix_fgets(buf, len, fp)
81 char   *buf;
82 int     len;
83 FILE   *fp;
84 {
85     char   *cp = buf;
86     int     c;
87 
88     /*
89      * Copy until the buffer fills up, until EOF, or until a newline is
90      * found.
91      */
92     while (len > 1 && (c = getc(fp)) != EOF) {
93 	len--;
94 	*cp++ = c;
95 	if (c == '\n')
96 	    break;
97     }
98 
99     /*
100      * Return 0 if nothing was read. This is correct even when a silly buffer
101      * length was specified.
102      */
103     if (cp > buf) {
104 	*cp = 0;
105 	return (buf);
106     } else {
107 	return (0);
108     }
109 }
110 
111 #endif /* BROKEN_FGETS */
112 
113  /*
114   * With early SunOS 5 versions, recvfrom() does not completely fill in the
115   * source address structure when doing a non-destructive read. The following
116   * code works around the problem. It does no harm on "normal" systems.
117   */
118 
119 #ifdef RECVFROM_BUG
120 
121 #undef recvfrom
122 
123 int     fix_recvfrom(sock, buf, buflen, flags, from, fromlen)
124 int     sock;
125 char   *buf;
126 int     buflen;
127 int     flags;
128 struct sockaddr *from;
129 int    *fromlen;
130 {
131     int     ret;
132 
133     /* Assume that both ends of a socket belong to the same address family. */
134 
135     if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) {
136 	if (from->sa_family == 0) {
137 	    struct sockaddr my_addr;
138 	    int     my_addr_len = sizeof(my_addr);
139 
140 	    if (getsockname(0, &my_addr, &my_addr_len)) {
141 		tcpd_warn("getsockname: %m");
142 	    } else {
143 		from->sa_family = my_addr.sa_family;
144 	    }
145 	}
146     }
147     return (ret);
148 }
149 
150 #endif /* RECVFROM_BUG */
151 
152  /*
153   * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an
154   * error in case of a datagram-oriented socket. Instead, they claim that all
155   * UDP requests come from address 0.0.0.0. The following code works around
156   * the problem. It does no harm on "normal" systems.
157   */
158 
159 #ifdef GETPEERNAME_BUG
160 
161 #undef getpeername
162 
163 int     fix_getpeername(sock, sa, len)
164 int     sock;
165 struct sockaddr *sa;
166 int    *len;
167 {
168     int     ret;
169     struct sockaddr_in *sin = (struct sockaddr_in *) sa;
170 
171     if ((ret = getpeername(sock, sa, len)) >= 0
172 	&& sa->sa_family == AF_INET
173 	&& sin->sin_addr.s_addr == 0) {
174 	errno = ENOTCONN;
175 	return (-1);
176     } else {
177 	return (ret);
178     }
179 }
180 
181 #endif /* GETPEERNAME_BUG */
182 
183  /*
184   * According to Karl Vogel (vogelke@c-17igp.wpafb.af.mil) some Pyramid
185   * versions have no yp_default_domain() function. We use getdomainname()
186   * instead.
187   */
188 
189 #ifdef USE_GETDOMAIN
190 
191 int     yp_get_default_domain(ptr)
192 char  **ptr;
193 {
194     static char mydomain[MAXHOSTNAMELEN];
195 
196     *ptr = mydomain;
197     return (getdomainname(mydomain, MAXHOSTNAMELEN));
198 }
199 
200 #endif /* USE_GETDOMAIN */
201 
202 #ifndef INADDR_NONE
203 #define INADDR_NONE 0xffffffff
204 #endif
205 
206  /*
207   * Solaris 2.4 gethostbyname() has problems with multihomed hosts. When
208   * doing DNS through NIS, only one host address ends up in the address list.
209   * All other addresses end up in the hostname alias list, interspersed with
210   * copies of the official host name. This would wreak havoc with tcpd's
211   * hostname double checks. Below is a workaround that should do no harm when
212   * accidentally left in. A side effect of the workaround is that address
213   * list members are no longer properly aligned for structure access.
214   */
215 
216 #ifdef SOLARIS_24_GETHOSTBYNAME_BUG
217 
218 #undef gethostbyname
219 
220 struct hostent *fix_gethostbyname(name)
221 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(buf, sep)
259 char   *buf;
260 char   *sep;
261 {
262     static char *state;
263     char   *result;
264 
265     if (buf)
266 	state = buf;
267     while ((result = strsep(&state, sep)) && result[0] == 0)
268 	 /* void */ ;
269     return (result);
270 }
271 
272 #endif /* USE_STRSEP */
273 
274  /*
275   * IRIX 5.3 (and possibly earlier versions, too) library routines call the
276   * non-reentrant strtok() library routine, causing hosts to slip through
277   * allow/deny filters. Workaround: don't rely on the vendor and use our own
278   * strtok() function. FreeBSD 2.0 has a similar problem (fixed in 2.0.5).
279   */
280 
281 #ifdef LIBC_CALLS_STRTOK
282 
283 char   *my_strtok(buf, sep)
284 char   *buf;
285 char   *sep;
286 {
287     static char *state;
288     char   *result;
289 
290     if (buf)
291 	state = buf;
292 
293     /*
294      * Skip over separator characters and detect end of string.
295      */
296     if (*(state += strspn(state, sep)) == 0)
297 	return (0);
298 
299     /*
300      * Skip over non-separator characters and terminate result.
301      */
302     result = state;
303     if (*(state += strcspn(state, sep)) != 0)
304 	*state++ = 0;
305     return (result);
306 }
307 
308 #endif /* LIBC_CALLS_STRTOK */
309