 /*
  * Workarounds for known system software bugs. This module provides wrappers
  * around library functions and system calls that are known to have problems
  * on some systems. Most of these workarounds won't do any harm on regular
  * systems.
  * 
  * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  */

#ifndef lint
char    sccsid[] = "@(#) workarounds.c 1.1 93/09/21 23:16:38";
#endif

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <syslog.h>

#include "log_tcp.h"

 /*
  * Some DG/UX inet_addr() versions return a struct/union instead of a long.
  * You have this problem when the compiler complains about illegal lvalues
  * or something like that. The following code fixes this mutant behaviour.
  * It should not be enabled on "normal" systems.
  * 
  * Bug reported by ben@piglet.cr.usgs.gov (Rev. Ben A. Mesander).
  */

#ifdef INET_ADDR_BUG

#undef inet_addr

long    fix_inet_addr(string)
char   *string;
{
    return (inet_addr(string).s_addr);
}

#endif /* INET_ADDR_BUG */

 /*
  * With some System-V versions, the fgets() library function does not
  * account for partial reads from e.g. sockets. The result is that fgets()
  * gives up too soon, causing username lookups to fail. Problem first
  * reported for IRIX 4.0.5, by Steve Kotsopoulos <steve@ecf.toronto.edu>.
  * The following code works around the problem. It does no harm on "normal"
  * systems.
  */

#ifdef BROKEN_FGETS

#undef fgets

char   *fix_fgets(buf, len, fp)
char   *buf;
int     len;
FILE   *fp;
{
    char   *cp = buf;
    int     c;

    /*
     * Copy until the buffer fills up, until EOF, or until a newline is
     * found.
     */
    while (len > 1 && (c = getc(fp)) != EOF) {
	len--;
	*cp++ = c;
	if (c == '\n')
	    break;
    }

    /*
     * Return 0 if nothing was read. This is correct even when a silly buffer
     * length was specified.
     */
    if (cp > buf) {
	*cp = 0;
	return (buf);
    } else {
	return (0);
    }
}

#endif /* BROKEN_FGETS */

 /*
  * With early SunOS 5 versions, recvfrom() does not completely fill in the
  * source address structure when doing a non-destructive read. The following
  * code works around the problem. It does no harm on "normal" systems.
  */

#ifdef RECVFROM_BUG

#undef recvfrom

int     fix_recvfrom(sock, buf, buflen, flags, from, fromlen)
int     sock;
char   *buf;
int     buflen;
int     flags;
struct sockaddr *from;
int    *fromlen;
{
    int     ret;

    /* Assume that both ends of a socket belong to the same address family. */

    if ((ret = recvfrom(sock, buf, buflen, flags, from, fromlen)) >= 0) {
	if (from->sa_family == 0) {
	    struct sockaddr my_addr;
	    int     my_addr_len = sizeof(my_addr);

	    if (getsockname(0, &my_addr, &my_addr_len)) {
		syslog(LOG_ERR, "getsockname: %m");
	    } else {
		from->sa_family = my_addr.sa_family;
	    }
	}
    }
    return (ret);
}

#endif /* RECVFROM_BUG */

 /*
  * The Apollo SR10.3 and some SYSV4 getpeername(2) versions do not return an
  * error in case of a datagram-oriented socket. Instead, they claim that all
  * UDP requests come from address 0.0.0.0. The following code works around
  * the problem. It does no harm on "normal" systems.
  */

#ifdef GETPEERNAME_BUG

#undef getpeername

int     fix_getpeername(sock, sa, len)
int     sock;
struct sockaddr *sa;
int    *len;
{
    int     ret;
    struct sockaddr_in *sin = (struct sockaddr_in *) sa;

    if ((ret = getpeername(sock, sa, len)) >= 0
	&& sa->sa_family == AF_INET
	&& sin->sin_addr.s_addr == 0) {
	errno = ENOTCONN;
	return (-1);
    } else {
	return (ret);
    }
}

#endif /* GETPEERNAME_BUG */
