#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <dlfcn.h>

/*
 * Test program for socket_wrapper
 * $ cc test.c -o test -ldl
 * $ mkdir sockets
 * $ LD_PRELOAD=libsocket_wrapper.so SOCKET_WRAPPER_DIR=sockets \
 *   SOCKET_WRAPPER_DEBUGLEVEL=4 LD_DEBUG=symbols ./test
 */
int main(int argc, char **argv)
{
    int s, fd;
    ssize_t n;
    char buf[] = "aaa";
    void *h;
    char *dlerr;
    int (*closefn)(int);

    /*
     * Emulate the following call sequence that happens in glibc resolver
     * s = socket() -> handled by swrap_socket()
     * connect() -> fail with ECONNREFUSED (omit connect() here)
     * internal glibc close(s) -> s is left lingering in swrap.
     */
    s = socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0);
    if (s < 0) {
        printf("socket err %s\n", strerror(errno));
        exit(1);
    }
    printf("socket() opened %d\n", s);

    /* use an internal close for s */
    h = dlopen("libc.so.6", RTLD_LAZY);
    if (h == NULL) {
        printf("dlopen() error %s\n", dlerror());
        exit(1);
    }
    *(void **)(&closefn) = dlsym(h, "close");
    if ((dlerr = dlerror()) != NULL) {
        printf("dlsym err %s\n", dlerr);
        exit(1);
    }
    closefn(s);

    /* If swrap_open() handled this it would remove the stale fd.
     * The expected failure happens when this is a libc open()
     */
    fd = open("file", O_CREAT|O_WRONLY|O_TRUNC, 0600);
    if (fd == -1) {
        printf("open err %s\n", strerror(errno));
        exit(1);
    }
    printf("open() returned %d\n", fd);

    /* write() ENOTCONN means we wrote to stale fd. */
    n = write(fd, buf, strlen(buf) + 1);
    if (n != strlen(buf) + 1) {
        if (errno == 107) {
            printf("Got expected failure: %s\n", strerror(errno));
            exit(0);
        }
    } else {
        printf("write OK\n");
    }

    exit(0);
}

