/* Copyright (c) 1995,1996,1997 NEC Corporation.  All rights reserved.       */
/*                                                                           */
/* The redistribution, use and modification in source or binary forms of     */
/* this software is subject to the conditions set forth in the copyright     */
/* document ("Copyright") included with this distribution.                   */

/*
 * $Id: wrap.c,v 1.53.2.1.2.4 1998/06/29 22:42:50 salman Exp $
 */

#define HIDEORIG
#include "socks5p.h"
#include "protocol.h"
#include "buffer.h"
#include "addr.h"
#include "wrap.h"
#include "wrap_tcp.h"
#include "wrap_udp.h"
#include "confutil.h"
#include "cache.h"
#include "hostname.h"
#include "log.h"

int lsInRLDFunctions = 0;
int lsInWrapFunction = 0;

/* This macro (w/o do...while(0)) scares me, but it gets rid of a lot of     */
/* warnings with Solaris's cc.                                               */
#define DLRETURN(var, rval, exp) (rval) = (exp); (var) = 0; return (rval)

static int lsSocketType(S5IOHandle fd, const ss *name) {
    int optval, optlen = sizeof(int), slen = sizeof(S5NetAddr), eno = GETERRNO();
    S5NetAddr na;

    memset(&na, 0, sizeof(na));

    if (REAL(getsockname)(fd, &na.sa, &slen) < 0) return -1;

    if ((!ISINET(&na.sa) && !ISUNSPEC(&na.sa)) || (ISUNSPEC(&na.sa) && (!name || !ISINET(name)))) return -1;

    if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&optval, &optlen) < 0) return -1;

    SETERRNO(eno);
    return optval;
}

/* wrapper around the shutdown system call.  This could be kind of tricky:   */
/* may be we should keep track of SHUTDOWN status as read or write may be    */
/* still allowed even after shutdown, depending on the value of howto XXX    */
int LIBPREFIX(shutdown)(S5IOHandle fd, int howto) {
#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(shutdown)(fd, howto);
#endif
    if (!lsConnectionCached(fd)) return REAL(shutdown)(fd, howto);

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");
    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS shutdown: FAKE");
    lsConnectionDel(fd);
    lsInWrapFunction = 0;

    return REAL(shutdown)(fd, howto);
}

/* wrapper around the connect system call.                                   */
int LIBPREFIX(connect)(S5IOHandle sd, CONST ss *name, int namelen) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(connect)(sd, (ss *)name, namelen);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, name)) {
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS connect: FAKE");
        DLRETURN(lsInWrapFunction, rval, lsUdpConnect(sd, name, namelen));
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS connect: FAKE");
        DLRETURN(lsInWrapFunction, rval, lsTcpConnect(sd, name, namelen));
    default:
        S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS connect: REAL");
        DLRETURN(lsInWrapFunction, rval, REAL(connect)(sd, (ss *)name, namelen));
    }
}

/* wrapper around the bind system call.                                      */
int LIBPREFIX(bind)(S5IOHandle sd, CONST ss *name, int namelen) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(bind)(sd, (ss *)name, namelen);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, name)) {
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS bind: FAKE");
        DLRETURN(lsInWrapFunction, rval, lsTcpBind(sd, name, namelen));
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS bind: FAKE");
        DLRETURN(lsInWrapFunction, rval, lsUdpBind(sd, name, namelen));
    default:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS bind: REAL");
        DLRETURN(lsInWrapFunction, rval, REAL(bind)(sd, (ss *)name, namelen));
    }
}

/* wrapper around the getsockname system call.                               */
int LIBPREFIX(getsockname)(S5IOHandle sd, struct sockaddr *name, int *namelen) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(getsockname)(sd, name, namelen);
#endif
    if (!lsConnectionCached(sd)) return REAL(getsockname)(sd, name, namelen);

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, NULL)) {
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS getsockname: FAKE");
        DLRETURN(lsInWrapFunction, rval, lsTcpGetsockname(sd, name, namelen));
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS getsockname: FAKE");
        DLRETURN(lsInWrapFunction, rval, lsUdpGetsockname(sd, name, namelen));
    default:
        S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS getsockname: REAL");
        DLRETURN(lsInWrapFunction, rval, REAL(getsockname)(sd, name, namelen));
    }
}

/* wrapper around the getpeername system call.                               */
int LIBPREFIX(getpeername)(S5IOHandle sd, struct sockaddr *name, int *namelen) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(getpeername)(sd, name, namelen);
#endif
    if (!lsConnectionCached(sd)) return REAL(getpeername)(sd, name, namelen);

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, NULL)) {
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS getpeername: FAKE");
        DLRETURN(lsInWrapFunction, rval, lsUdpGetpeername(sd, name, namelen)); 
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS getpeername: FAKE");
        DLRETURN(lsInWrapFunction, rval, lsTcpGetpeername(sd, name, namelen));
    default:
        S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS getpeername: REAL");
        DLRETURN(lsInWrapFunction, rval, REAL(getpeername)(sd, name, namelen));
    }
}

/* wrapper around the send system call.                                      */
IORETTYPE LIBPREFIX(send)(S5IOHandle sd, const IOPTRTYPE msg, IOLENTYPE len, int flags) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(send)(sd, msg, len, flags);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, NULL)) {
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS write: FAKE: Udp");
        DLRETURN(lsInWrapFunction, rval, lsUdpSend(sd, msg, len, flags));
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS write: FAKE: Tcp");
        DLRETURN(lsInWrapFunction, rval, lsTcpSend(sd, msg, len, flags));
    default:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS send: REAL: Wrong type");	
        DLRETURN(lsInWrapFunction, rval, -1);
    }
}

/* wrapper around the sendto system call.                                    */
IORETTYPE LIBPREFIX(sendto)(S5IOHandle sd, const IOPTRTYPE msg, IOLENTYPE len, int flags, struct sockaddr *to, int tolen) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(sendto)(sd, msg, len, flags, to, tolen);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, NULL)) {
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS sendto: FAKE: Udp");
        DLRETURN(lsInWrapFunction, rval, lsUdpSendto(sd, msg, len, flags, to, tolen));
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS sendto: FAKE: Tcp");
        DLRETURN(lsInWrapFunction, rval, lsTcpSend(sd, msg, len, flags));
    default:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS sendto: REAL: Wrong type");	
        DLRETURN(lsInWrapFunction, rval, -1);
    }
}

/* wrapper around the recv system call.                                      */
IORETTYPE LIBPREFIX(recv)(S5IOHandle sd, IOPTRTYPE msg, IOLENTYPE len, int flags) {
    int rval;
    
#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(recv)(sd, msg, len, flags);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, NULL)) {
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS recv: FAKE: Udp");
        DLRETURN(lsInWrapFunction, rval, lsUdpRecvfrom(sd, msg, len, flags, NULL, NULL, 1));
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS recv: FAKE: Tcp");
        DLRETURN(lsInWrapFunction, rval, lsTcpRecv(sd, msg, len, flags));
    default:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS recv: REAL: Wrong type");	
		SETSOCKETERROR(EBADF);
        DLRETURN(lsInWrapFunction, rval, -1);
    }
}

/* wrapper around the recvfrom system call.                                  */
IORETTYPE 
LIBPREFIX(recvfrom)(S5IOHandle sd, IOPTRTYPE msg, IOLENTYPE len, int flags, ss *from, int *fromlen) 
{
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(recvfrom)(sd, msg, len, flags, from, fromlen);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, NULL)) {

		case SOCK_DGRAM:
			S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0,\
						"SOCKS recv: FAKE: Udp");
			DLRETURN(lsInWrapFunction, rval, \
					lsUdpRecvfrom(sd, msg, len, flags, from, fromlen, 0));
    	case SOCK_STREAM:
        	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS recv: FAKE: Tcp");
        	DLRETURN(lsInWrapFunction, rval, \
						lsTcpRecvfrom(sd, msg, len, flags, from, fromlen));
    	default:
			S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0,\
						"SOCKS recv: REAL: Wrong type");	
			SETSOCKETERROR(EBADF);
			DLRETURN(lsInWrapFunction, rval, -1);
    }
}

/* wrapper around the recvmsg system call.                                  */

IORETTYPE
LIBPREFIX(recvmsg)(S5IOHandle sd, ms *msg, int flags)
{
int rval;
 
#ifdef FOR_SHARED_LIBRARY
    if(lsInRLDFunctions || lsInWrapFunction)
     return REAL(recvmsg)(sd, msg, flags);
#endif
 
    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");
 
    switch (lsSocketType(sd, NULL)) {
		case SOCK_DGRAM:
            S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0,\
						"SOCKS recvmsg: FAKE: Udp");
            DLRETURN(lsInWrapFunction, rval, lsUdpRecvmsg(sd, msg, flags));

        case SOCK_STREAM:
            S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0,\
						"SOCKS recvmsg: FAKE: Tcp");
            DLRETURN(lsInWrapFunction, rval, lsTcpRecvmsg(sd, msg, flags));

        default:
            S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0,\
						"SOCKS recvmsg: REAL: Wrong type");
			SETSOCKETERROR(EBADF);
            DLRETURN(lsInWrapFunction, rval, -1);
    }
}

/* wrapper around the sendmsg system call.                                  */
IORETTYPE
LIBPREFIX(sendmsg)(S5IOHandle sd,ms *msg, int flags)
{
int rval;
 
#ifdef FOR_SHARED_LIBRARY
    if(lsInRLDFunctions || lsInWrapFunction)
     return REAL(sendmsg)(sd, msg, flags);
#endif
 
    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");
 
    switch (lsSocketType(sd, NULL)) {

        case SOCK_DGRAM:
            S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0,\
						"SOCKS sendmsg: FAKE: Udp");
            DLRETURN(lsInWrapFunction, rval, lsUdpSendmsg(sd, msg, flags));

        case SOCK_STREAM:
            S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0,\
						"SOCKS sendmsg: FAKE: Tcp");
            DLRETURN(lsInWrapFunction, rval, lsTcpSendmsg(sd, msg, flags));

        default:
            S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0,\
						"SOCKS sendmsg: REAL: Wrong type");
			SETSOCKETERROR(EBADF);
            DLRETURN(lsInWrapFunction, rval, -1);
    }
}



/* wrapper around the close system call.  Not strictly necessary, but it     */
/* cleans up some things, so it is kind of nice to call...                   */
int LIBPREFIX(close)(S5IOHandle fd) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(close)(fd);
#endif
    if (!lsConnectionCached(fd)) return REAL(close)(fd);

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");
    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS close: FAKE");

    if (getenv("SOCKS5_PRESERVE_STDERR") && fd == STDERR_FILENO) {
	DLRETURN(lsInWrapFunction, rval, 0);
    }

    lsConnectionDel(fd);
    DLRETURN(lsInWrapFunction, rval, REAL(close)(fd));
}

/* wrapper around the fclose stdio library function...See comment for close. */
int LIBPREFIX(fclose)(FILE *fp) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction || fp == NULL) return REAL(fclose)(fp);
#endif
    if (!lsConnectionCached(fileno(fp))) return REAL(fclose)(fp);

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");
    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS fclose: FAKE");

    if (getenv("SOCKS5_PRESERVE_STDERR") && fileno(fp) == STDERR_FILENO) {
	DLRETURN(lsInWrapFunction, rval, 0);
    }

    lsConnectionDel(fileno(fp));
    DLRETURN(lsInWrapFunction, rval, REAL(fclose)(fp));
}

IORETTYPE LIBPREFIX(write)(S5IOHandle sd, const IOPTRTYPE buf, IOLENTYPE buflen) {
    int rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(write)(sd, buf, buflen);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, NULL)) {
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS write: FAKE: Udp");
	DLRETURN(lsInWrapFunction, rval, lsUdpSend(sd, buf, buflen, 0));
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS write: FAKE: Tcp");
	DLRETURN(lsInWrapFunction, rval, lsTcpSend(sd, buf, buflen, 0));
    default:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS write: REAL: Wrong type");	
	DLRETURN(lsInWrapFunction, rval, REAL(write)(sd, buf, buflen));
    }
}

IORETTYPE LIBPREFIX(read)(S5IOHandle sd, IOPTRTYPE buf, IOLENTYPE buflen) {
    int rval;
    
#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(read)(sd, buf, buflen);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    switch (lsSocketType(sd, NULL)) {
    case SOCK_DGRAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS read: FAKE: Udp");
	DLRETURN(lsInWrapFunction, rval, lsUdpRecvfrom(sd, buf, buflen, 0, NULL, NULL, 1));
    case SOCK_STREAM:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS read: FAKE: Tcp");
	DLRETURN(lsInWrapFunction, rval, lsTcpRecv(sd, buf, buflen, 0));
    default:
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS read: REAL: Wrong type");	
	DLRETURN(lsInWrapFunction, rval, REAL(read)(sd, buf, buflen));
    }
}

S5IOHandle LIBPREFIX(dup)(S5IOHandle sd) {
    lsSocksInfo *pcon, *ncon, *q;
    lsProxyInfo *pri;
    S5IOHandle s2;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(dup)(sd);
#endif
    if (!lsConnectionCached(sd)) return REAL(dup)(sd);

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    /* Solaris (who knows why) occasionally gives you back a different type  */
    /* of file descriptor after a DUP (e.g. SOCK_STREAM vs SOCK_DGRAM).  It  */
    /* seems to mistakenly leave whatever label was there beforehand.  So,   */
    /* we'll dup2 over a socket that we know is the right type instead...    */
    /*                                                                       */
    /* XXX Proposed new fix - Not tested - blob                              */
#if defined(sun) && defined(__svr4__)
    {
    	S5IOHandle tmpsd;
	int type = lsSocketType(sd, NULL);

    	if (type == -1 || (tmpsd = socket(AF_INET, type, 0)) == S5InvalidIOHandle) {
	    s2 = REAL(dup)(sd);
	} else if ((s2 = REAL(dup2)(sd, tmpsd)) == S5InvalidIOHandle) {
    	    REAL(close)(tmpsd);
	}
    }
#else
    s2 = REAL(dup)(sd);
#endif
    if (s2 == S5InvalidIOHandle) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup: dup failed: %m");
    	lsInWrapFunction = 0;
	return S5InvalidIOHandle;
    }
    
    if ((pcon = lsConnectionFind(sd)) == NULL) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup: no connection found");
    	lsInWrapFunction = 0;
	return s2;
    }

    if ((ncon = lsConnectionFind(s2)) != NULL) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup: deleting invalid connection found");
	lsConnectionDel(s2);
    }

    if ((ncon = lsConnectionAdd(s2)) == NULL) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup: couldn't add connection");
	REAL(close)(s2);
	SETSOCKETERROR(EMFILE);
    	lsInWrapFunction = 0;
	return S5InvalidIOHandle;
    }

    q = ncon->next;
    *ncon = *pcon;
    ncon->fd = s2;
    ncon->next = q;

    for (pri = ncon->pri; pri; pri = pri->next) pri->refcount++;
    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup: done");
    lsInWrapFunction = 0;
    return s2;
}

S5IOHandle LIBPREFIX(dup2)(S5IOHandle sd, S5IOHandle s2) {
    lsSocksInfo *pcon, *ncon, *q;
    lsProxyInfo *pri;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(dup2)(sd, s2);
#endif
    if (!lsConnectionCached(sd)) return REAL(dup2)(sd, s2);

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");

    if (getenv("SOCKS5_PRESERVE_STDERR") && s2 == STDERR_FILENO) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup2: leaving stderr alone (by request)");	
    	lsInWrapFunction = 0;
	return s2;
    }

    if ((s2 = REAL(dup2)(sd, s2)) == S5InvalidIOHandle) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup2: dup2 failed");	
    	lsInWrapFunction = 0;
	return S5InvalidIOHandle;
    }
    
    if ((pcon = lsConnectionFind(sd)) == NULL) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup2: no connection found");	
    	lsInWrapFunction = 0;
	return s2;
    }

    if ((ncon = lsConnectionFind(s2)) != NULL) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup2: deleting invalid connection found");
	lsConnectionDel(s2);
    }

    if ((ncon = lsConnectionAdd(s2)) == NULL) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup2: couldn't add connection");
	REAL(close)(s2);
	SETSOCKETERROR(EMFILE);
    	lsInWrapFunction = 0;
	return S5InvalidIOHandle;
    }

    q = ncon->next;
    *ncon = *pcon;
    ncon->fd = s2;
    ncon->next = q;

    for (pri = ncon->pri; pri; pri = pri->next) pri->refcount++;
    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS dup2: done");
    lsInWrapFunction = 0;
    return s2;
}

struct tm *LIBPREFIX(localtime)(const time_t *clock) {
    struct tm *rval;

#ifdef FOR_SHARED_LIBRARY
    if (lsInRLDFunctions || lsInWrapFunction) return REAL(localtime)(clock);
#endif

    lsInWrapFunction = 1;
    LIBPREFIX2(init)("libsocks5");
    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS localtime: FAKE");

    rval = REAL(localtime)(clock);
    lsInWrapFunction = 0;
    return rval;
}

void LIBPREFIX(longjmp)(jmp_buf env, int val) {
#ifdef FOR_SHARED_LIBRARY
    if (!lsInWrapHostname) lsInWrapFunction = 0;
#endif

    LIBPREFIX2(init)("libsocks5");
    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "SOCKS longjmp: FAKE");
    REAL(longjmp)(env, val);
}

#ifndef FOR_SHARED_LIBRARY
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#define VA_START(a, b) va_start((a), (b))
#define va_alist ...
#define va_dcl
#else
#include <varargs.h>
#define VA_START(a, b) va_start((a))
#endif

int LIBPREFIX(fprintf)(FILE *fp, const char *format, va_alist) va_dcl {
    char buf[2048];
    va_list pvar;
    int retval, optval;
    int optlen = sizeof(int);
    S5IOHandle sd = fileno(fp);

#ifdef HAVE_STDARG_H
    va_start(pvar, format);
#else
    VA_START(pvar, format);
#endif

    if (getsockopt(fileno(fp), SOL_SOCKET, SO_TYPE, (char *)&optval, &optlen) < 0)
	retval = REAL(vfprintf)(fp, format, pvar);
    else {
	vsprintf(buf, format, pvar);
	retval = LIBPREFIX(write)(sd, buf, strlen(buf));
    }

    va_end(pvar);
    return retval;
}

int LIBPREFIX(vfprintf)(FILE *fp, const char *format, va_list pvar) {
    char buf[2048];
    int optval;
    int optlen = sizeof(int);
    S5IOHandle sd = fileno(fp);

    if (getsockopt(fileno(fp), SOL_SOCKET, SO_TYPE, (char *)&optval, &optlen) < 0)
	return REAL(vfprintf)(fp, format, pvar);
    else {
	vsprintf(buf, format, pvar);
	return LIBPREFIX(write)(sd, buf, strlen(buf));
    }
}

int LIBPREFIX(getc)(FILE *fp) {
    u_char c;
    int optval;
    int optlen = sizeof(int);
    S5IOHandle sd = fileno(fp);

    if (getsockopt(fileno(fp), SOL_SOCKET, SO_TYPE, (char *)&optval, &optlen) < 0)
	return REAL(getc)(fp);
    else {
	if (LIBPREFIX(read)(sd, &c, 1) != 1) return EOF;
	return (int)c;
    }
}

#endif /* FOR_SHARED_LIBRARY */

