ds3.0/bin/
ds3.0/extra/
ds3.0/extra/crat/
ds3.0/extra/creremote/
ds3.0/extra/mingw/
ds3.0/extra/wolfpaw/
ds3.0/fluffos-2.18-ds07/
ds3.0/fluffos-2.18-ds07/Win32/
ds3.0/fluffos-2.18-ds07/compat/
ds3.0/fluffos-2.18-ds07/compat/simuls/
ds3.0/fluffos-2.18-ds07/testsuite/
ds3.0/fluffos-2.18-ds07/testsuite/clone/
ds3.0/fluffos-2.18-ds07/testsuite/command/
ds3.0/fluffos-2.18-ds07/testsuite/data/
ds3.0/fluffos-2.18-ds07/testsuite/etc/
ds3.0/fluffos-2.18-ds07/testsuite/include/
ds3.0/fluffos-2.18-ds07/testsuite/inherit/
ds3.0/fluffos-2.18-ds07/testsuite/inherit/master/
ds3.0/fluffos-2.18-ds07/testsuite/log/
ds3.0/fluffos-2.18-ds07/testsuite/single/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/compiler/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/efuns/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/operators/
ds3.0/fluffos-2.18-ds07/testsuite/u/
ds3.0/fluffos-2.18-ds07/tmp/
ds3.0/lib/cmds/admins/
ds3.0/lib/cmds/common/
ds3.0/lib/cmds/creators/include/
ds3.0/lib/daemon/services/
ds3.0/lib/daemon/tmp/
ds3.0/lib/doc/
ds3.0/lib/doc/bguide/
ds3.0/lib/doc/efun/all/
ds3.0/lib/doc/efun/arrays/
ds3.0/lib/doc/efun/buffers/
ds3.0/lib/doc/efun/compile/
ds3.0/lib/doc/efun/floats/
ds3.0/lib/doc/efun/functions/
ds3.0/lib/doc/efun/mixed/
ds3.0/lib/doc/efun/numbers/
ds3.0/lib/doc/efun/parsing/
ds3.0/lib/doc/help/classes/
ds3.0/lib/doc/help/races/
ds3.0/lib/doc/lfun/
ds3.0/lib/doc/lfun/all/
ds3.0/lib/doc/lfun/lib/abilities/
ds3.0/lib/doc/lfun/lib/armor/
ds3.0/lib/doc/lfun/lib/bank/
ds3.0/lib/doc/lfun/lib/bot/
ds3.0/lib/doc/lfun/lib/clay/
ds3.0/lib/doc/lfun/lib/clean/
ds3.0/lib/doc/lfun/lib/clerk/
ds3.0/lib/doc/lfun/lib/client/
ds3.0/lib/doc/lfun/lib/combat/
ds3.0/lib/doc/lfun/lib/connect/
ds3.0/lib/doc/lfun/lib/container/
ds3.0/lib/doc/lfun/lib/corpse/
ds3.0/lib/doc/lfun/lib/creator/
ds3.0/lib/doc/lfun/lib/daemon/
ds3.0/lib/doc/lfun/lib/damage/
ds3.0/lib/doc/lfun/lib/deterioration/
ds3.0/lib/doc/lfun/lib/donate/
ds3.0/lib/doc/lfun/lib/door/
ds3.0/lib/doc/lfun/lib/equip/
ds3.0/lib/doc/lfun/lib/file/
ds3.0/lib/doc/lfun/lib/fish/
ds3.0/lib/doc/lfun/lib/fishing/
ds3.0/lib/doc/lfun/lib/flashlight/
ds3.0/lib/doc/lfun/lib/follow/
ds3.0/lib/doc/lfun/lib/ftp_client/
ds3.0/lib/doc/lfun/lib/ftp_data_connection/
ds3.0/lib/doc/lfun/lib/fuel/
ds3.0/lib/doc/lfun/lib/furnace/
ds3.0/lib/doc/lfun/lib/genetics/
ds3.0/lib/doc/lfun/lib/holder/
ds3.0/lib/doc/lfun/lib/id/
ds3.0/lib/doc/lfun/lib/interactive/
ds3.0/lib/doc/lfun/lib/lamp/
ds3.0/lib/doc/lfun/lib/leader/
ds3.0/lib/doc/lfun/lib/light/
ds3.0/lib/doc/lfun/lib/limb/
ds3.0/lib/doc/lfun/lib/living/
ds3.0/lib/doc/lfun/lib/load/
ds3.0/lib/doc/lfun/lib/look/
ds3.0/lib/doc/lfun/lib/manipulate/
ds3.0/lib/doc/lfun/lib/meal/
ds3.0/lib/doc/lfun/lib/messages/
ds3.0/lib/doc/lfun/lib/player/
ds3.0/lib/doc/lfun/lib/poison/
ds3.0/lib/doc/lfun/lib/position/
ds3.0/lib/doc/lfun/lib/post_office/
ds3.0/lib/doc/lfun/lib/potion/
ds3.0/lib/doc/lfun/lib/room/
ds3.0/lib/doc/lfun/lib/server/
ds3.0/lib/doc/lfun/lib/spell/
ds3.0/lib/doc/lfun/lib/torch/
ds3.0/lib/doc/lfun/lib/vendor/
ds3.0/lib/doc/lfun/lib/virt_sky/
ds3.0/lib/doc/lfun/lib/weapon/
ds3.0/lib/doc/lfun/lib/worn_storage/
ds3.0/lib/doc/lpc/constructs/
ds3.0/lib/doc/lpc/etc/
ds3.0/lib/doc/lpc/intermediate/
ds3.0/lib/doc/lpc/types/
ds3.0/lib/doc/misc/
ds3.0/lib/doc/old/
ds3.0/lib/doc/phints/
ds3.0/lib/domains/
ds3.0/lib/domains/Praxis/adm/
ds3.0/lib/domains/Praxis/attic/
ds3.0/lib/domains/Praxis/cemetery/mon/
ds3.0/lib/domains/Praxis/data/
ds3.0/lib/domains/Praxis/death/
ds3.0/lib/domains/Praxis/mountains/
ds3.0/lib/domains/Praxis/obj/armour/
ds3.0/lib/domains/Praxis/obj/magic/
ds3.0/lib/domains/Praxis/obj/weapon/
ds3.0/lib/domains/Praxis/orc_valley/
ds3.0/lib/domains/Ylsrim/
ds3.0/lib/domains/Ylsrim/adm/
ds3.0/lib/domains/Ylsrim/armor/
ds3.0/lib/domains/Ylsrim/broken/
ds3.0/lib/domains/Ylsrim/fish/
ds3.0/lib/domains/Ylsrim/meal/
ds3.0/lib/domains/Ylsrim/npc/
ds3.0/lib/domains/Ylsrim/obj/
ds3.0/lib/domains/Ylsrim/virtual/
ds3.0/lib/domains/Ylsrim/weapon/
ds3.0/lib/domains/campus/adm/
ds3.0/lib/domains/campus/chamber/
ds3.0/lib/domains/campus/etc/
ds3.0/lib/domains/campus/meals/
ds3.0/lib/domains/campus/txt/ai/charles/
ds3.0/lib/domains/campus/txt/ai/charles/bak2/
ds3.0/lib/domains/campus/txt/ai/charles/bak2/bak1/
ds3.0/lib/domains/campus/txt/ai/charly/
ds3.0/lib/domains/campus/txt/ai/charly/bak/
ds3.0/lib/domains/campus/txt/jenny/
ds3.0/lib/domains/cave/doors/
ds3.0/lib/domains/cave/etc/
ds3.0/lib/domains/cave/meals/
ds3.0/lib/domains/cave/weap/
ds3.0/lib/domains/default/chamber/
ds3.0/lib/domains/default/creator/
ds3.0/lib/domains/default/doors/
ds3.0/lib/domains/default/etc/
ds3.0/lib/domains/default/vehicle/
ds3.0/lib/domains/default/virtual/
ds3.0/lib/domains/town/save/
ds3.0/lib/domains/town/txt/shame/
ds3.0/lib/domains/town/virtual/
ds3.0/lib/domains/town/virtual/bottom/
ds3.0/lib/domains/town/virtual/space/
ds3.0/lib/estates/
ds3.0/lib/ftp/
ds3.0/lib/lib/comp/
ds3.0/lib/lib/daemons/
ds3.0/lib/lib/daemons/include/
ds3.0/lib/lib/lvs/
ds3.0/lib/lib/user/
ds3.0/lib/lib/virtual/
ds3.0/lib/log/
ds3.0/lib/log/adm/
ds3.0/lib/log/archive/
ds3.0/lib/log/chan/
ds3.0/lib/log/errors/
ds3.0/lib/log/law/adm/
ds3.0/lib/log/law/email/
ds3.0/lib/log/law/names/
ds3.0/lib/log/law/sites-misc/
ds3.0/lib/log/law/sites-register/
ds3.0/lib/log/law/sites-tempban/
ds3.0/lib/log/law/sites-watch/
ds3.0/lib/log/open/
ds3.0/lib/log/reports/
ds3.0/lib/log/router/
ds3.0/lib/log/secure/
ds3.0/lib/log/watch/
ds3.0/lib/obj/book_source/
ds3.0/lib/obj/include/
ds3.0/lib/powers/prayers/
ds3.0/lib/powers/spells/
ds3.0/lib/realms/template/
ds3.0/lib/realms/template/adm/
ds3.0/lib/realms/template/area/
ds3.0/lib/realms/template/area/armor/
ds3.0/lib/realms/template/area/npc/
ds3.0/lib/realms/template/area/obj/
ds3.0/lib/realms/template/area/room/
ds3.0/lib/realms/template/area/weap/
ds3.0/lib/realms/template/bak/
ds3.0/lib/realms/template/cmds/
ds3.0/lib/save/kills/o/
ds3.0/lib/secure/cfg/classes/
ds3.0/lib/secure/cmds/builders/
ds3.0/lib/secure/cmds/creators/include/
ds3.0/lib/secure/cmds/players/
ds3.0/lib/secure/cmds/players/include/
ds3.0/lib/secure/daemon/imc2server/
ds3.0/lib/secure/daemon/include/
ds3.0/lib/secure/lib/
ds3.0/lib/secure/lib/include/
ds3.0/lib/secure/lib/net/include/
ds3.0/lib/secure/lib/std/
ds3.0/lib/secure/log/adm/
ds3.0/lib/secure/log/bak/
ds3.0/lib/secure/log/intermud/
ds3.0/lib/secure/log/network/
ds3.0/lib/secure/modules/
ds3.0/lib/secure/npc/
ds3.0/lib/secure/obj/include/
ds3.0/lib/secure/room/
ds3.0/lib/secure/save/
ds3.0/lib/secure/save/backup/
ds3.0/lib/secure/save/boards/
ds3.0/lib/secure/save/players/g/
ds3.0/lib/secure/tmp/
ds3.0/lib/secure/upgrades/files/
ds3.0/lib/secure/verbs/creators/
ds3.0/lib/std/board/
ds3.0/lib/std/lib/
ds3.0/lib/verbs/admins/include/
ds3.0/lib/verbs/builders/
ds3.0/lib/verbs/common/
ds3.0/lib/verbs/common/include/
ds3.0/lib/verbs/creators/
ds3.0/lib/verbs/creators/include/
ds3.0/lib/verbs/rooms/
ds3.0/lib/verbs/rooms/include/
ds3.0/lib/www/client/
ds3.0/lib/www/errors/
ds3.0/lib/www/images/
ds3.0/win32/
/*
 * socket_efun.c -- socket efuns for MudOS.
 *    5-92 : Dwayne Fontenot (Jacques@TMI) : original coding.
 *   10-92 : Dave Richards (Cynosure) : less original coding.
 */

#include "std.h"
#include "socket_efuns.h"
#include "socket_err.h"
#include "include/socket_err.h"
#include "socket_ctrl.h"
#include "comm.h"
#include "file.h"
#include "master.h"

#if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL)

/* flags for socket_close */
#define SC_FORCE        1
#define SC_DO_CALLBACK  2
#define SC_FINAL_CLOSE  4

lpc_socket_t *lpc_socks = 0;
int max_lpc_socks = 0;

#ifdef PACKAGE_SOCKETS
#ifdef IPV6
static int socket_name_to_sin (const char *, struct sockaddr_in6 *);
static char *inet_address (struct sockaddr_in6 *);
#else
static int socket_name_to_sin (const char *, struct sockaddr_in *);
static char *inet_address (struct sockaddr_in *);
#endif
#endif

/*
 * check permission
 */
int check_valid_socket (const char * const what, int fd, object_t * owner,
        const char * const addr, int port)
{
    array_t *info;
    svalue_t *mret;

    info = allocate_empty_array(4);
    info->item[0].type = T_NUMBER;
    info->item[0].u.number = fd;
    assign_socket_owner(&info->item[1], owner);
    info->item[2].type = T_STRING;
    info->item[2].subtype = STRING_SHARED;
    info->item[2].u.string = make_shared_string(addr);
    info->item[3].type = T_NUMBER;
    info->item[3].u.number = port;

    push_object(current_object);
    push_constant_string(what);
    push_refed_array(info);

    mret = apply_master_ob(APPLY_VALID_SOCKET, 3);
    return MASTER_APPROVED(mret);
}

static void clear_socket (int which, int dofree)
{
    if (dofree) {
        set_read_callback(which, 0);
        set_write_callback(which, 0);
        set_close_callback(which, 0);
    }

    lpc_socks[which].fd = -1;
    lpc_socks[which].flags = 0;
    lpc_socks[which].mode = MUD;
    lpc_socks[which].state = STATE_CLOSED;
    memset((char *) &lpc_socks[which].l_addr, 0, sizeof(lpc_socks[which].l_addr));
    memset((char *) &lpc_socks[which].r_addr, 0, sizeof(lpc_socks[which].r_addr));
    lpc_socks[which].owner_ob = NULL;
    lpc_socks[which].release_ob = NULL;
    lpc_socks[which].read_callback.s = 0;
    lpc_socks[which].write_callback.s = 0;
    lpc_socks[which].close_callback.s = 0;
    lpc_socks[which].r_buf = NULL;
    lpc_socks[which].r_off = 0;
    lpc_socks[which].r_len = 0;
    lpc_socks[which].w_buf = NULL;
    lpc_socks[which].w_off = 0;
    lpc_socks[which].w_len = 0;
}

/*
 * Get more LPC sockets structures if we run out
 */
static int more_lpc_sockets()
{
    int i;

    max_lpc_socks += 10;

    if (!lpc_socks)
        lpc_socks = CALLOCATE(10, lpc_socket_t, TAG_SOCKETS, "more_lpc_sockets");
    else
        lpc_socks = RESIZE(lpc_socks, max_lpc_socks, lpc_socket_t, TAG_SOCKETS, "more_lpc_sockets");

    i = max_lpc_socks;
    while (--i >= max_lpc_socks - 10)
        clear_socket(i, 0);

    return max_lpc_socks - 10;
}

/*
 * Set the callbacks for a socket
 */
void set_read_callback (int which, svalue_t * cb)
{
    char *s;

    if (lpc_socks[which].flags & S_READ_FP) {
        free_funp(lpc_socks[which].read_callback.f);
        lpc_socks[which].flags &= ~S_READ_FP;
    } else if ((s = lpc_socks[which].read_callback.s))
        free_string(s);

    if (cb) {
        if (cb->type == T_FUNCTION) {
            lpc_socks[which].flags |= S_READ_FP;
            lpc_socks[which].read_callback.f = cb->u.fp;
            cb->u.fp->hdr.ref++;
        } else {
            lpc_socks[which].read_callback.s = make_shared_string(cb->u.string);
        }
    } else
        lpc_socks[which].read_callback.s = 0;
}

void set_write_callback (int which, svalue_t * cb)
{
    char *s;

    if (lpc_socks[which].flags & S_WRITE_FP) {
        free_funp(lpc_socks[which].write_callback.f);
        lpc_socks[which].flags &= ~S_WRITE_FP;
    } else if ((s = lpc_socks[which].write_callback.s))
        free_string(s);

    if (cb) {
        if (cb->type == T_FUNCTION) {
            lpc_socks[which].flags |= S_WRITE_FP;
            lpc_socks[which].write_callback.f = cb->u.fp;
            cb->u.fp->hdr.ref++;
        } else {
            lpc_socks[which].write_callback.s = make_shared_string(cb->u.string);
        }
    } else
        lpc_socks[which].write_callback.s = 0;
}

void set_close_callback (int which, svalue_t * cb)
{
    char *s;

    if (lpc_socks[which].flags & S_CLOSE_FP) {
        free_funp(lpc_socks[which].close_callback.f);
        lpc_socks[which].flags &= ~S_CLOSE_FP;
    } else if ((s = lpc_socks[which].close_callback.s))
        free_string(s);

    if (cb) {
        if (cb->type == T_FUNCTION) {
            lpc_socks[which].flags |= S_CLOSE_FP;
            lpc_socks[which].close_callback.f = cb->u.fp;
            cb->u.fp->hdr.ref++;
        } else {
            lpc_socks[which].close_callback.s = make_shared_string(cb->u.string);
        }
    } else
        lpc_socks[which].close_callback.s = 0;
}

#ifdef PACKAGE_SOCKETS

static void copy_close_callback (int to, int from)
{
    char *s;

    if (lpc_socks[to].flags & S_CLOSE_FP) {
        free_funp(lpc_socks[to].close_callback.f);
    } else if ((s = lpc_socks[to].close_callback.s))
        free_string(s);

    if (lpc_socks[from].flags & S_CLOSE_FP) {
        lpc_socks[to].flags |= S_CLOSE_FP;
        lpc_socks[to].close_callback.f = lpc_socks[from].close_callback.f;
        lpc_socks[to].close_callback.f->hdr.ref++;
    } else {
        lpc_socks[to].flags &= ~S_CLOSE_FP;
        s = lpc_socks[to].close_callback.s = lpc_socks[from].close_callback.s;
        if (s)
            ref_string(s);
    }
}

#endif

int find_new_socket (void)
{
    int i;

    for (i = 0; i < max_lpc_socks; i++) {
        if (lpc_socks[i].state == STATE_CLOSED)
            return i;
    }
    return more_lpc_sockets();
}

#ifdef PACKAGE_SOCKETS

/*
 * Create an LPC efun socket
 */
int socket_create (enum socket_mode mode, svalue_t * read_callback, svalue_t * close_callback)
{
    int type, i, fd, optval;
#ifndef NO_BUFFER_TYPE
    int binary = 0;

    if (mode == STREAM_BINARY) {
        binary = 1;
        mode = STREAM;
    } else if (mode == DATAGRAM_BINARY) {
        binary = 1;
        mode = DATAGRAM;
    }
#endif
    switch (mode) {

        case MUD:
        case STREAM:
            type = SOCK_STREAM;
            break;
        case DATAGRAM:
            type = SOCK_DGRAM;
            break;

        default:
            return EEMODENOTSUPP;
    }

    i = find_new_socket();
    if (i >= 0) {
#ifdef IPV6
        fd = socket(PF_INET6, type, 0);
#else
        fd = socket(PF_INET, type, 0);
#endif
        if (fd == INVALID_SOCKET) {
            socket_perror("socket_create: socket", 0);
            return EESOCKET;
        }
        optval = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optval,
                    sizeof(optval)) == -1) {
            socket_perror("socket_create: setsockopt", 0);
            OS_socket_close(fd);
            return EESETSOCKOPT;
        } 
        if (set_socket_nonblocking(fd, 1) == -1) {
            socket_perror("socket_create: set_socket_nonblocking", 0);
            OS_socket_close(fd);
            return EENONBLOCK;
        }
        lpc_socks[i].fd = fd;
        lpc_socks[i].flags = S_HEADER;

        if (type == SOCK_DGRAM) close_callback = 0;
        set_read_callback(i, read_callback);
        set_write_callback(i, 0);
        set_close_callback(i, close_callback);

#ifndef NO_BUFFER_TYPE
        if (binary) {
            lpc_socks[i].flags |= S_BINARY;
        }
#endif
        lpc_socks[i].mode = mode;
        lpc_socks[i].state = STATE_UNBOUND;
        memset((char *) &lpc_socks[i].l_addr, 0, sizeof(lpc_socks[i].l_addr));
        memset((char *) &lpc_socks[i].r_addr, 0, sizeof(lpc_socks[i].r_addr));
        lpc_socks[i].owner_ob = current_object;
        lpc_socks[i].release_ob = NULL;
        lpc_socks[i].r_buf = NULL;
        lpc_socks[i].r_off = 0;
        lpc_socks[i].r_len = 0;
        lpc_socks[i].w_buf = NULL;
        lpc_socks[i].w_off = 0;
        lpc_socks[i].w_len = 0;

        current_object->flags |= O_EFUN_SOCKET;

        debug(sockets, ("socket_create: created socket %d mode %d fd %d\n",
                    i, mode, fd));
    }

    return i;
}

/*
 * Bind an address to an LPC efun socket
 */
int socket_bind (int fd, int port, const char * addr)
{
    socklen_t len;
#ifdef IPV6
    struct sockaddr_in6 sin;
#else
    struct sockaddr_in sin;
#endif
    if (fd < 0 || fd >= max_lpc_socks)
        return EEFDRANGE;
    if (lpc_socks[fd].state == STATE_CLOSED ||
            lpc_socks[fd].state == STATE_FLUSHING)
        return EEBADF;
    if (lpc_socks[fd].owner_ob != current_object)
        return EESECURITY;
    if (lpc_socks[fd].state != STATE_UNBOUND)
        return EEISBOUND;
#ifdef IPV6
    sin.sin6_family = AF_INET6;
#else
    sin.sin_family = AF_INET;
#endif
    if (!addr) {
        if (MUD_IP[0])
#ifdef IPV6
            inet_pton(AF_INET6, MUD_IP, &(sin.sin6_addr));
        else
            sin.sin6_addr = in6addr_any;
        sin.sin6_port = htons((u_short) port);
#else
        sin.sin_addr.s_addr = inet_addr(MUD_IP);
        else
            sin.sin_addr.s_addr = INADDR_ANY;
        sin.sin_port = htons((u_short) port);
#endif
    } else {
        if (!socket_name_to_sin(addr, &sin))
            return EEBADADDR;
    }

    if (bind(lpc_socks[fd].fd, (struct sockaddr *) & sin, sizeof(sin)) == -1) {
        switch (socket_errno) {
            case EADDRINUSE:
                return EEADDRINUSE;
            default:
                socket_perror("socket_bind: bind", 0);
                return EEBIND;
        }
    }
    len = sizeof(sin);
    if (getsockname(lpc_socks[fd].fd, (struct sockaddr *) & lpc_socks[fd].l_addr, &len) == -1) {
        socket_perror("socket_bind: getsockname", 0);
        return EEGETSOCKNAME;
    }
    lpc_socks[fd].state = STATE_BOUND;

#ifdef IPV6
    char tmp[INET6_ADDRSTRLEN];
    debug(sockets, ("socket_bind: bound socket %d to %s.%d\n",
                fd, inet_ntop(AF_INET6, &lpc_socks[fd].l_addr.sin6_addr, &tmp, INET6_ADDRSTRLEN),
                ntohs(lpc_socks[fd].l_addr.sin6_port)));

#else
    debug(sockets, ("socket_bind: bound socket %d to %s.%d\n",
                fd, inet_ntoa(lpc_socks[fd].l_addr.sin_addr),
                ntohs(lpc_socks[fd].l_addr.sin_port)));
#endif
    return EESUCCESS;
}

/*
 * Listen for connections on an LPC efun socket
 */
int socket_listen (int fd, svalue_t * callback)
{
    if (fd < 0 || fd >= max_lpc_socks)
        return EEFDRANGE;
    if (lpc_socks[fd].state == STATE_CLOSED ||
            lpc_socks[fd].state == STATE_FLUSHING)
        return EEBADF;
    if (lpc_socks[fd].owner_ob != current_object)
        return EESECURITY;
    if (lpc_socks[fd].mode == DATAGRAM)
        return EEMODENOTSUPP;
    if (lpc_socks[fd].state == STATE_UNBOUND)
        return EENOADDR;
    if (lpc_socks[fd].state != STATE_BOUND)
        return EEISCONN;

    if (listen(lpc_socks[fd].fd, 5) == -1) {
        socket_perror("socket_listen: listen", 0);
        return EELISTEN;
    }
    lpc_socks[fd].state = STATE_LISTEN;
    set_read_callback(fd, callback);

    current_object->flags |= O_EFUN_SOCKET;

    debug(sockets, ("socket_listen: listen on socket %d\n", fd));

    return EESUCCESS;
}

/*
 * Accept a connection on an LPC efun socket
 */
int socket_accept (int fd, svalue_t * read_callback, svalue_t * write_callback)
{
    int accept_fd, i;
    socklen_t len;
#ifdef IPV6
    struct sockaddr_in6 sin;
#else
    struct sockaddr_in sin;
#endif
    if (fd < 0 || fd >= max_lpc_socks)
        return EEFDRANGE;
    if (lpc_socks[fd].state == STATE_CLOSED ||
            lpc_socks[fd].state == STATE_FLUSHING)
        return EEBADF;
    if (lpc_socks[fd].owner_ob != current_object)
        return EESECURITY;
    if (lpc_socks[fd].mode == DATAGRAM)
        return EEMODENOTSUPP;
    if (lpc_socks[fd].state != STATE_LISTEN)
        return EENOTLISTN;

    lpc_socks[fd].flags &= ~S_WACCEPT;

    len = sizeof(sin);
    accept_fd = accept(lpc_socks[fd].fd, (struct sockaddr *) & sin, &len);
    if (accept_fd == -1) {
        switch (socket_errno) {
#ifdef EWOULDBLOCK
            case EWOULDBLOCK:
                return EEWOULDBLOCK;
#endif
            case EINTR:
                return EEINTR;
            default:
                socket_perror("socket_accept: accept", 0);
                return EEACCEPT;
        }
    }

    /*
     * according to Amylaar, 'accepted' sockets in Linux 0.99p6 don't
     * properly inherit the nonblocking property from the listening socket.
     * Marius, 19-Jun-2000: this happens on other platforms as well, so just
     * do it for everyone
     */
    if (set_socket_nonblocking(accept_fd, 1) == -1) {
        socket_perror("socket_accept: set_socket_nonblocking 1", 0);
        OS_socket_close(accept_fd);
        return EENONBLOCK;
    }

    i = find_new_socket();
    if (i >= 0) {
        fd_set wmask;
        struct timeval t;
        int nb;

        lpc_socks[i].fd = accept_fd;
        lpc_socks[i].flags = S_HEADER |
            (lpc_socks[fd].flags & S_BINARY);

        FD_ZERO(&wmask);
        FD_SET(accept_fd, &wmask);
        t.tv_sec = 0;
        t.tv_usec = 0;
#ifndef hpux
        nb = select(FD_SETSIZE, (fd_set *) 0, &wmask, (fd_set *) 0, &t);
#else
        nb = select(FD_SETSIZE, (int *) 0, (int *) &wmask, (int *) 0, &t);
#endif
        //if (!(FD_ISSET(accept_fd, &wmask)))
        lpc_socks[i].flags |= S_BLOCKED;

        lpc_socks[i].mode = lpc_socks[fd].mode;
        lpc_socks[i].state = STATE_DATA_XFER;
        len = sizeof(sin);
        if (getsockname(lpc_socks[i].fd, (struct sockaddr *)&lpc_socks[i].l_addr, &len) == -1) {
            lpc_socks[i].l_addr = lpc_socks[fd].l_addr;
        }
        lpc_socks[i].r_addr = sin;
        lpc_socks[i].owner_ob = NULL;
        lpc_socks[i].release_ob = NULL;
        lpc_socks[i].r_buf = NULL;
        lpc_socks[i].r_off = 0;
        lpc_socks[i].r_len = 0;
        lpc_socks[i].w_buf = NULL;
        lpc_socks[i].w_off = 0;
        lpc_socks[i].w_len = 0;

        lpc_socks[i].owner_ob = current_object;
        set_read_callback(i, read_callback);
        set_write_callback(i, write_callback);
        copy_close_callback(i, fd);

        current_object->flags |= O_EFUN_SOCKET;

        debug(sockets, ("socket_accept: accept on socket %d\n", fd));
        debug(sockets, ("socket_accept: new socket %d on fd %d\n", i, accept_fd));
    } else
        OS_socket_close(accept_fd);

    return i;
}

/*
 * Connect an LPC efun socket
 */
int socket_connect (int fd, const char * name, svalue_t * read_callback, svalue_t * write_callback)
{
    if (fd < 0 || fd >= max_lpc_socks)
        return EEFDRANGE;
    if (lpc_socks[fd].state == STATE_CLOSED ||
            lpc_socks[fd].state == STATE_FLUSHING)
        return EEBADF;
    if (lpc_socks[fd].owner_ob != current_object)
        return EESECURITY;
    if (lpc_socks[fd].mode == DATAGRAM)
        return EEMODENOTSUPP;
    switch (lpc_socks[fd].state) {
        case STATE_CLOSED:
        case STATE_FLUSHING:
        case STATE_UNBOUND:
        case STATE_BOUND:
            break;
        case STATE_LISTEN:
            return EEISLISTEN;
        case STATE_DATA_XFER:
            return EEISCONN;
    }

    if (!socket_name_to_sin(name, &lpc_socks[fd].r_addr))
        return EEBADADDR;

    set_read_callback(fd, read_callback);
    set_write_callback(fd, write_callback);

    current_object->flags |= O_EFUN_SOCKET;

#ifdef WINSOCK
    /* Turn on blocking for connect to ensure correct errors */
    if (set_socket_nonblocking(lpc_socks[fd].fd, 0) == -1) {
        socket_perror("socket_connect: set_socket_nonblocking 0", 0);
        OS_socket_close(fd);
        return EENONBLOCK;
    }
#endif
    if (connect(lpc_socks[fd].fd, (struct sockaddr *) & lpc_socks[fd].r_addr,
#ifdef IPV6
                sizeof(struct sockaddr_in6)) == -1) {
#else
        sizeof(struct sockaddr_in)) == -1) {
#endif
            switch (socket_errno) {
                case EINTR:
                    return EEINTR;
                case EADDRINUSE:
                    return EEADDRINUSE;
                case EALREADY:
                    return EEALREADY;
                case ECONNREFUSED:
                    return EECONNREFUSED;
                case EINPROGRESS:
                    break;
                default:
                    socket_perror("socket_connect: connect", 0);
                    return EECONNECT;
            }
        }
#ifdef WINSOCK
        if (set_socket_nonblocking(lpc_socks[fd].fd, 1) == -1) {
            socket_perror("socket_connect: set_socket_nonblocking 1", 0);
            OS_socket_close(fd);
            return EENONBLOCK;
        }
#endif
        lpc_socks[fd].state = STATE_DATA_XFER;
        lpc_socks[fd].flags |= S_BLOCKED;

        return EESUCCESS;
    }

    /*
     * Write a message on an LPC efun socket
     */
    int socket_write (int fd, svalue_t * message, const char * name)
    {
        int len, off;
        char *buf, *p;
#ifdef IPV6
        struct sockaddr_in6 sin;
#else
        struct sockaddr_in sin;
#endif
        if (fd < 0 || fd >= max_lpc_socks)
            return EEFDRANGE;
        if (lpc_socks[fd].state == STATE_CLOSED ||
                lpc_socks[fd].state == STATE_FLUSHING)
            return EEBADF;
        if (lpc_socks[fd].owner_ob != current_object)
            return EESECURITY;
        if (lpc_socks[fd].mode == DATAGRAM) {
            if (name == NULL)
                return EENOADDR;
            if (!socket_name_to_sin(name, &sin))
                return EEBADADDR;
        } else {
            if (lpc_socks[fd].state != STATE_DATA_XFER)
                return EENOTCONN;
            if (name != NULL)
                return EEBADADDR;
            if (lpc_socks[fd].flags & S_BLOCKED)
                return EEALREADY;
        }

        switch (lpc_socks[fd].mode) {

            case MUD:
                switch (message->type) {

                    case T_OBJECT:
                        return EETYPENOTSUPP;

                    default:
                        save_svalue_depth = 0;
                        len = svalue_save_size(message);
                        if (save_svalue_depth > MAX_SAVE_SVALUE_DEPTH) {
                            return EEBADDATA;
                        }
                        buf = (char *)
                            DMALLOC(len + 5, TAG_TEMPORARY, "socket_write: default");
                        if (buf == NULL)
                            fatal("Out of memory");
                        *(INT_32 *) buf = htonl((long) len);
                        len += 4;
                        buf[4] = '\0';
                        p = buf + 4;
                        save_svalue(message, &p);
                        break;
                }
                break;

            case STREAM:
                switch (message->type) {
#ifndef NO_BUFFER_TYPE
                    case T_BUFFER:
                        len = message->u.buf->size;
                        buf = (char *) DMALLOC(len, TAG_TEMPORARY, "socket_write: T_BUFFER");
                        if (buf == NULL)
                            fatal("Out of memory");
                        memcpy(buf, message->u.buf->item, len);
                        break;
#endif
                    case T_STRING:
                        len = SVALUE_STRLEN(message);
                        buf = (char *) DMALLOC(len + 1, TAG_TEMPORARY, "socket_write: T_STRING");
                        if (buf == NULL)
                            fatal("Out of memory");
                        strcpy(buf, message->u.string);
                        break;
                    case T_ARRAY:
                        {
                            int i, limit;
                            svalue_t *el;

                            len = message->u.arr->size * sizeof(int);
                            buf = (char *) DMALLOC(len + 1, TAG_TEMPORARY, "socket_write: T_ARRAY");
                            if (buf == NULL)
                                fatal("Out of memory");
                            el = message->u.arr->item;
                            limit = len / sizeof(int);
                            for (i = 0; i < limit; i++) {
                                switch (el[i].type) {
                                    case T_NUMBER:
                                        memcpy((char *) &buf[i * sizeof(int)],
                                                (char *) &el[i].u.number, sizeof(int));
                                        break;
                                    case T_REAL:
                                        memcpy((char *) &buf[i * sizeof(int)], (char *) &el[i].u.real,
                                                sizeof(int));
                                        break;
                                    default:
                                        break;
                                }
                            }
                            break;
                        }
                    default:
                        return EETYPENOTSUPP;
                }
                break;

            case DATAGRAM:
                switch (message->type) {

                    case T_STRING:
                        if ((off = sendto(lpc_socks[fd].fd, (char *)message->u.string,
                                        strlen(message->u.string) + 1, 0,
                                        (struct sockaddr *) & sin, sizeof(sin))) == -1) {
                            //socket_perror("socket_write: sendto", 0);
                            return EESENDTO;
                        }
                        break;

#ifndef NO_BUFFER_TYPE
                    case T_BUFFER:
                        if ((off = sendto(lpc_socks[fd].fd, (char *)message->u.buf->item,
                                        message->u.buf->size, 0,
                                        (struct sockaddr *) & sin, sizeof(sin))) == -1) {
                            //socket_perror("socket_write: sendto", 0);
                            return EESENDTO;
                        }
                        break;
#endif

                    default:
                        return EETYPENOTSUPP;
                }

#ifdef F_NETWORK_STATS
                if (!(lpc_socks[fd].flags & S_EXTERNAL)) {
                    inet_out_packets++;
                    inet_out_volume += off;
                    inet_socket_out_packets++;
                    inet_socket_out_volume += off;
                }
#endif
                return EESUCCESS;

            default:
                return EEMODENOTSUPP;
        }

        if (!len) {
            FREE(buf);
            return EESUCCESS;
        }
        off = OS_socket_write(lpc_socks[fd].fd, buf, len);
        if (off <= 0) {
            FREE(buf);

#ifdef EWOULDBLOCK
            if (off == -1 && socket_errno == EWOULDBLOCK)
                return EEWOULDBLOCK;
#endif
            if (off == -1 && socket_errno == EINTR)
                return EEINTR;

            //socket_perror("socket_write: send", 0);
            lpc_socks[fd].flags |= S_LINKDEAD;
            socket_close(fd, SC_FORCE | SC_DO_CALLBACK | SC_FINAL_CLOSE);
            return EESEND;
        }

#ifdef F_NETWORK_STATS
        if (!(lpc_socks[fd].flags & S_EXTERNAL)) {
            inet_out_packets++;
            inet_out_volume += off;
            inet_socket_out_packets++;
            inet_socket_out_volume += off;
        }
#endif

        if (off < len) {
            lpc_socks[fd].flags |= S_BLOCKED;
            lpc_socks[fd].w_buf = buf;
            lpc_socks[fd].w_off = off;
            lpc_socks[fd].w_len = len - off;
            return EECALLBACK;
        }
        FREE(buf);

        return EESUCCESS;
    }

#endif  /* PACKAGE_SOCKETS */

    static void call_callback (int fd, int what, int num_arg)
    {
        union string_or_func callback;

        switch (what) {
            case S_READ_FP: callback = lpc_socks[fd].read_callback; break;
            case S_WRITE_FP: callback = lpc_socks[fd].write_callback; break;
            case S_CLOSE_FP: callback = lpc_socks[fd].close_callback; break;
        }

        if (lpc_socks[fd].flags & what) {
            safe_call_function_pointer(callback.f, num_arg);
        } else if (callback.s) {
            if (callback.s[0] == APPLY___INIT_SPECIAL_CHAR)
                error("Illegal function name.\n");
            safe_apply(callback.s, lpc_socks[fd].owner_ob, num_arg, ORIGIN_INTERNAL);
        }
    }

    /*
     * Handle LPC efun socket read select events
     */
    void socket_read_select_handler (int fd)
    {
        int cc = 0;
        socklen_t addrlen;
        char buf[BUF_SIZE], addr[ADDR_BUF_SIZE];
        svalue_t value;
#ifdef IPV6
        struct sockaddr_in6 sin;
#else
        struct sockaddr_in sin;
#endif
        debug(sockets, ("read_socket_handler: fd %d state %d\n",
                    fd, lpc_socks[fd].state));

        switch (lpc_socks[fd].state) {

            case STATE_CLOSED:
            case STATE_FLUSHING:
                return;

            case STATE_UNBOUND:
                debug_message("socket_read_select_handler: read on unbound socket %i\n", fd);
                break;

            case STATE_BOUND:
                switch (lpc_socks[fd].mode) {

                    case MUD:
                    case STREAM:
                        break;

                    case DATAGRAM:
                        debug(sockets, ("read_socket_handler: DATA_XFER DATAGRAM\n"));
                        addrlen = sizeof(sin);
                        cc = recvfrom(lpc_socks[fd].fd, buf, sizeof(buf) - 1, 0,
                                (struct sockaddr *) & sin, &addrlen);
                        if (cc <= 0)
                            break;
#ifdef F_NETWORK_STATS
                        if (!(lpc_socks[fd].flags & S_EXTERNAL)) {
                            inet_in_packets++;
                            inet_in_volume += cc;
                            inet_socket_in_packets++;
                            inet_socket_in_volume++;
                        }
#endif
                        debug(sockets, ("read_socket_handler: read %d bytes\n", cc));
                        buf[cc] = '\0';
#ifdef IPV6
                        char tmp[INET6_ADDRSTRLEN];
                        sprintf(addr, "%s %d", inet_ntop(AF_INET6, &sin.sin6_addr, tmp, INET6_ADDRSTRLEN),
                                ntohs(sin.sin6_port));
#else
                        sprintf(addr, "%s %d", inet_ntoa(sin.sin_addr),
                                ntohs(sin.sin_port));
#endif
                        push_number(fd);
#ifndef NO_BUFFER_TYPE
                        if (lpc_socks[fd].flags & S_BINARY) {
                            buffer_t *b;

                            b = allocate_buffer(cc);
                            if (b) {
                                memcpy(b->item, buf, cc);
                                push_refed_buffer(b);
                            } else {
                                push_number(0);
                            }
                        } else {
#endif
                            copy_and_push_string(buf);
#ifndef NO_BUFFER_TYPE
                        }
#endif
                        copy_and_push_string(addr);
                        debug(sockets, ("read_socket_handler: apply\n"));
                        call_callback(fd, S_READ_FP, 3);
                        return;
                    case STREAM_BINARY:
                    case DATAGRAM_BINARY:
                        ;
                }
                break;

            case STATE_LISTEN:
                debug(sockets, ("read_socket_handler: apply read callback\n"));
                lpc_socks[fd].flags |= S_WACCEPT;
                push_number(fd);
                call_callback(fd, S_READ_FP, 1);
                return;

            case STATE_DATA_XFER:
                switch (lpc_socks[fd].mode) {

                    case DATAGRAM:
                        break;

                    case MUD:
                        debug(sockets, ("read_socket_handler: DATA_XFER MUD\n"));
                        if (lpc_socks[fd].flags & S_HEADER) {
                            cc = OS_socket_read(lpc_socks[fd].fd, (char *) &lpc_socks[fd].r_len +
                                    lpc_socks[fd].r_off, 4 - lpc_socks[fd].r_off);
                            if (cc <= 0)
                                break;
#ifdef F_NETWORK_STATS
                            if (!(lpc_socks[fd].flags & S_EXTERNAL)) {
                                inet_in_packets++;
                                inet_in_volume += cc;
                                inet_socket_in_packets++;
                                inet_socket_in_volume += cc;
                            }
#endif
                            debug(sockets, ("read_socket_handler: read %d bytes\n", cc));
                            lpc_socks[fd].r_off += cc;
                            if (lpc_socks[fd].r_off != 4)
                                return;
                            debug(sockets, ("read_socket_handler: read header\n"));
                            lpc_socks[fd].flags &= ~S_HEADER;
                            lpc_socks[fd].r_off = 0;
                            lpc_socks[fd].r_len = ntohl(lpc_socks[fd].r_len);
                            if (lpc_socks[fd].r_len <= 0 || lpc_socks[fd].r_len > MAX_BYTE_TRANSFER)
                                break;
                            lpc_socks[fd].r_buf = (char *)
                                DMALLOC(lpc_socks[fd].r_len + 1, TAG_TEMPORARY, "socket_read_select_handler");
                            if (lpc_socks[fd].r_buf == NULL)
                                fatal("Out of memory");
                            debug(sockets, ("read_socket_handler: svalue len is %lu\n",
                                        lpc_socks[fd].r_len));
                        }
                        if (lpc_socks[fd].r_off < lpc_socks[fd].r_len) {
                            cc = OS_socket_read(lpc_socks[fd].fd, lpc_socks[fd].r_buf +
                                    lpc_socks[fd].r_off, lpc_socks[fd].r_len -
                                    lpc_socks[fd].r_off);
                            if (cc <= 0)
                                break;
#ifdef F_NETWORK_STATS
                            if (!(lpc_socks[fd].flags & S_EXTERNAL)) {
                                inet_in_packets++;
                                inet_in_volume += cc;
                                inet_socket_in_packets++;
                                inet_socket_in_volume += cc;
                            }
#endif
                            debug(sockets, ("read_socket_handler: read %d bytes\n", cc));
                            lpc_socks[fd].r_off += cc;
                            if (lpc_socks[fd].r_off != lpc_socks[fd].r_len)
                                return;
                            debug(sockets, ("read_socket_handler: read svalue\n"));
                        }
                        lpc_socks[fd].r_buf[lpc_socks[fd].r_len] = '\0';
                        value = const0;
                        push_number(fd);
                        if (restore_svalue(lpc_socks[fd].r_buf, &value) == 0) {
                            STACK_INC;
                            *sp = value;
                        } else {
                            push_undefined();
                        }
                        FREE(lpc_socks[fd].r_buf);
                        lpc_socks[fd].flags |= S_HEADER;
                        lpc_socks[fd].r_buf = NULL;
                        lpc_socks[fd].r_off = 0;
                        lpc_socks[fd].r_len = 0;
                        debug(sockets, ("read_socket_handler: apply read callback\n"));
                        call_callback(fd, S_READ_FP, 2);
                        return;

                    case STREAM:
                        debug(sockets, ("read_socket_handler: DATA_XFER STREAM\n"));
                        cc = OS_socket_read(lpc_socks[fd].fd, buf, sizeof(buf) - 1);
                        if (cc <= 0)
                            break;
#ifdef F_NETWORK_STATS
                        if (!(lpc_socks[fd].flags & S_EXTERNAL)) {
                            inet_in_packets++;
                            inet_in_volume += cc;
                            inet_socket_in_packets++;
                            inet_socket_in_volume += cc;
                        }
#endif
                        debug(sockets, ("read_socket_handler: read %d bytes\n", cc));
                        buf[cc] = '\0';
                        push_number(fd);
#ifndef NO_BUFFER_TYPE
                        if (lpc_socks[fd].flags & S_BINARY) {
                            buffer_t *b;

                            b = allocate_buffer(cc);
                            if (b) {
                                b->ref--;
                                memcpy(b->item, buf, cc);
                                push_buffer(b);
                            } else {
                                push_number(0);
                            }
                        } else {
#endif
                            copy_and_push_string(buf);
#ifndef NO_BUFFER_TYPE
                        }
#endif
                        debug(sockets, ("read_socket_handler: apply read callback\n"));
                        call_callback(fd, S_READ_FP, 2);
                        return;
                    case STREAM_BINARY:
                    case DATAGRAM_BINARY:
                        ;
                }
                break;
        }
        if (cc == -1) {
            switch (socket_errno) {
                case ECONNREFUSED:
                    /* Evidentally, on Linux 1.2.1, ECONNREFUSED gets returned
                     * if an ICMP_PORT_UNREACHED error happens internally.  Why
                     * they use this error message, I have no idea, but this seems
                     * to work.
                     */
                    if (lpc_socks[fd].state == STATE_BOUND
                            && lpc_socks[fd].mode == DATAGRAM)
                        return;
                    break;
                case EINTR:
#ifdef EWOULDBLOCK
                case EWOULDBLOCK:
                    return;
#endif
                default:
                    break;
            }
        }

        lpc_socks[fd].flags |= S_LINKDEAD;
        socket_close(fd, SC_FORCE | SC_DO_CALLBACK | SC_FINAL_CLOSE);
    }

    /*
     * Handle LPC efun socket write select events
     */
    void socket_write_select_handler (int fd)
    {
        int cc;

        debug(sockets, ("write_socket_handler: fd %d state %d\n",
                    fd, lpc_socks[fd].state));

        /* if the socket isn't blocked, we've got nothing to send */
        /* if the socket is linkdead, don't send -- could block */
        if (!(lpc_socks[fd].flags & S_BLOCKED) || lpc_socks[fd].flags & S_LINKDEAD)
            return;

        if (lpc_socks[fd].w_buf != NULL) {
            cc = OS_socket_write(lpc_socks[fd].fd,
                    lpc_socks[fd].w_buf + lpc_socks[fd].w_off,
                    lpc_socks[fd].w_len);
            if (cc <= -1) {
                if (cc == -1 && (
#ifdef EWOULDBLOCK
                            errno == EWOULDBLOCK ||
#endif
                            errno == EINTR)) {
                    return;
                }

                lpc_socks[fd].flags |= S_LINKDEAD;
                if (lpc_socks[fd].state == STATE_FLUSHING) {
                    lpc_socks[fd].flags &= ~S_BLOCKED;
                    socket_close(fd, SC_FORCE | SC_FINAL_CLOSE);
                    return;
                }
                socket_close(fd, SC_FORCE | SC_DO_CALLBACK | SC_FINAL_CLOSE);
                return;
            }
#ifdef F_NETWORK_STATS
            if (!(lpc_socks[fd].flags & S_EXTERNAL)) {
                inet_out_packets++;
                inet_out_volume += cc;
                inet_socket_out_packets++;
                inet_socket_out_volume += cc;
            }
#endif
            lpc_socks[fd].w_off += cc;
            lpc_socks[fd].w_len -= cc;
            if (lpc_socks[fd].w_len != 0)
                return;
            FREE(lpc_socks[fd].w_buf);
            lpc_socks[fd].w_buf = NULL;
            lpc_socks[fd].w_off = 0;
        }
        lpc_socks[fd].flags &= ~S_BLOCKED;
        if (lpc_socks[fd].state == STATE_FLUSHING) {
            socket_close(fd, SC_FORCE | SC_FINAL_CLOSE);
            return;
        }

        debug(sockets, ("write_socket_handler: apply write_callback\n"));

        push_number(fd);
        call_callback(fd, S_WRITE_FP, 1);
    }

    /*
     * Close an LPC efun socket
     */
    int socket_close (int fd, int flags)
    {
        if (fd < 0 || fd >= max_lpc_socks)
            return EEFDRANGE;
        if (lpc_socks[fd].state == STATE_CLOSED)
            return EEBADF;
        if (lpc_socks[fd].state == STATE_FLUSHING && !(flags & SC_FINAL_CLOSE))
            return EEBADF;
        if (!(flags & SC_FORCE) && lpc_socks[fd].owner_ob != current_object)
            return EESECURITY;

        if (flags & SC_DO_CALLBACK) {
            debug(sockets, ("read_socket_handler: apply close callback\n"));
            push_number(fd);
            call_callback(fd, S_CLOSE_FP, 1);
        }

        set_read_callback(fd, 0);
        set_write_callback(fd, 0);
        set_close_callback(fd, 0);

        /* if we're linkdead, we'll never flush, so don't even try :-) */
        if ((lpc_socks[fd].flags & S_BLOCKED) && !(lpc_socks[fd].flags & S_LINKDEAD)) {
            /* Can't close now; we still have data to write.  Tell the mudlib
             * it is closed, but we really finish up later.
             */
            lpc_socks[fd].state = STATE_FLUSHING;
            return EESUCCESS;
        }

        while (OS_socket_close(lpc_socks[fd].fd) == -1 && socket_errno == EINTR)
            ;       /* empty while */
        if (lpc_socks[fd].r_buf != NULL)
            FREE(lpc_socks[fd].r_buf);
        if (lpc_socks[fd].w_buf != NULL)
            FREE(lpc_socks[fd].w_buf);
        clear_socket(fd, 1);

        debug(sockets, ("socket_close: closed fd %d\n", fd));
        return EESUCCESS;
    }

#ifdef PACKAGE_SOCKETS

    /*
     * Release an LPC efun socket to another object
     */
    int socket_release (int fd, object_t * ob, svalue_t * callback)
    {
        if (fd < 0 || fd >= max_lpc_socks)
            return EEFDRANGE;
        if (lpc_socks[fd].state == STATE_CLOSED ||
                lpc_socks[fd].state == STATE_FLUSHING)
            return EEBADF;
        if (lpc_socks[fd].owner_ob != current_object)
            return EESECURITY;
        if (lpc_socks[fd].flags & S_RELEASE)
            return EESOCKRLSD;

        lpc_socks[fd].flags |= S_RELEASE;
        lpc_socks[fd].release_ob = ob;

        push_number(fd);
        push_object(ob);

        if (callback->type == T_FUNCTION)
            safe_call_function_pointer(callback->u.fp, 2);
        else
            safe_apply(callback->u.string, ob, 2, ORIGIN_INTERNAL);

        if ((lpc_socks[fd].flags & S_RELEASE) == 0)
            return EESUCCESS;

        lpc_socks[fd].flags &= ~S_RELEASE;
        lpc_socks[fd].release_ob = NULL;

        return EESOCKNOTRLSD;
    }

    /*
     * Aquire an LPC efun socket from another object
     */
    int socket_acquire (int fd, svalue_t * read_callback, svalue_t * write_callback, svalue_t * close_callback)
    {
        if (fd < 0 || fd >= max_lpc_socks)
            return EEFDRANGE;
        if (lpc_socks[fd].state == STATE_CLOSED ||
                lpc_socks[fd].state == STATE_FLUSHING)
            return EEBADF;
        if ((lpc_socks[fd].flags & S_RELEASE) == 0)
            return EESOCKNOTRLSD;
        if (lpc_socks[fd].release_ob != current_object)
            return EESECURITY;

        lpc_socks[fd].flags &= ~S_RELEASE;
        lpc_socks[fd].owner_ob = current_object;
        lpc_socks[fd].release_ob = NULL;

        set_read_callback(fd, read_callback);
        set_write_callback(fd, write_callback);
        set_close_callback(fd, close_callback);

        return EESUCCESS;
    }

    /*
     * Return the string representation of a socket error
     */
    const char *socket_error (int error)
    {
        error = -(error + 1);
        if (error < 0 || error >= ERROR_STRINGS - 1)
            return "socket_error: invalid error number";
        return error_strings[error];
    }

    /*
     * Return the remote address for an LPC efun socket
     */
    int get_socket_address (int fd, char * addr, int * port, int local)
    {
#ifdef IPV6
        struct sockaddr_in6 *addr_in;
#else
        struct sockaddr_in *addr_in;
#endif
        if (fd < 0 || fd >= max_lpc_socks) {
            addr[0] = '\0';
            *port = 0;
            return EEFDRANGE;
        }
        addr_in = (local ? &lpc_socks[fd].l_addr : &lpc_socks[fd].r_addr);
#ifdef IPV6
        *port = ntohs(addr_in->sin6_port);
        inet_ntop(AF_INET6, &addr_in->sin6_addr, addr, INET6_ADDRSTRLEN);
#else
        *port = ntohs(addr_in->sin_port);
        strcpy(addr, inet_ntoa(addr_in->sin_addr));
#endif
        return EESUCCESS;
    }

    /*
     * Return the current socket owner
     */
    object_t *get_socket_owner (int fd)
    {
        if (fd < 0 || fd >= max_lpc_socks)
            return (object_t *) NULL;
        if (lpc_socks[fd].state == STATE_CLOSED ||
                lpc_socks[fd].state == STATE_FLUSHING)
            return (object_t *) NULL;
        return lpc_socks[fd].owner_ob;
    }

#endif  /* PACKAGE_SOCKETS */

    /*
     * Initialize a T_OBJECT svalue
     */
    void assign_socket_owner (svalue_t * sv, object_t * ob)
    {
        if (ob != NULL) {
            sv->type = T_OBJECT;
            sv->u.ob = ob;
            add_ref(ob, "assign_socket_owner");
        } else
            assign_svalue_no_free(sv, &const0u);
    }

#ifdef PACKAGE_SOCKETS

    /*
     * Convert a string representation of an address to a sockaddr_in
     */
#ifdef IPV6
    static int socket_name_to_sin (const char * name, struct sockaddr_in6 * sin)
#else
        static int socket_name_to_sin (const char * name, struct sockaddr_in * sin)
#endif
        {
            int port;
            char *cp, addr[ADDR_BUF_SIZE];

            strncpy(addr, name, ADDR_BUF_SIZE);
            addr[ADDR_BUF_SIZE - 1] = '\0';

            cp = strchr(addr, ' ');
            if (cp == NULL)
                return 0;

            *cp = '\0';
            port = atoi(cp + 1);

#ifdef IPV6
            sin->sin6_family = AF_INET6;
            struct addrinfo hints, *res;
            hints.ai_family = AF_INET6;
            hints.ai_socktype = 0;
            hints.ai_protocol = 0;
            hints.ai_flags = AI_CANONNAME| AI_V4MAPPED;

            if(getaddrinfo(addr, "1234", &hints, &res)){
                //failed
                socket_perror("socket_name_to_sin: getaddrinfo", 0);
                return 0;
            }
            struct sockaddr_in6 tmp;
            memcpy(&tmp, res->ai_addr, sizeof(tmp));
            freeaddrinfo(res);
            sin->sin6_addr = tmp.sin6_addr;
            sin->sin6_port = htons((u_short) port);
#else
            sin->sin_family = AF_INET;
            sin->sin_port = htons((u_short) port);
            sin->sin_addr.s_addr = inet_addr(addr);
#endif
            return 1;
        }

#endif  /* PACKAGE_SOCKETS */

    /*
     * Close any sockets owned by ob
     */
    void close_referencing_sockets (object_t * ob)
    {
        int i;

        for (i = 0; i < max_lpc_socks; i++)
            if (lpc_socks[i].owner_ob == ob &&
                    lpc_socks[i].state != STATE_CLOSED &&
                    lpc_socks[i].state != STATE_FLUSHING)
                socket_close(i, SC_FORCE);
    }

#ifdef PACKAGE_SOCKETS

    /*
     * Return the string representation of a sockaddr_in
     */
#ifdef IPV6
    static char *inet_address (struct sockaddr_in6 * sin)
#else
        static char *inet_address (struct sockaddr_in * sin)
#endif
        {
#ifdef IPV6
            static char addr[INET6_ADDRSTRLEN], port[7];
            if (!memcmp(&sin->sin6_addr, &in6addr_any, sizeof(in6addr_any)))
                strcpy(addr, "*");
            else
                inet_ntop(AF_INET6, &sin->sin6_addr, addr, INET6_ADDRSTRLEN);
            strcat(addr, ".");
            if (ntohs(sin->sin6_port) == 0)
                strcpy(port, "*");
            else
                sprintf(port, "%d", ntohs(sin->sin6_port));
            strcat(addr, port);
#else
            static char addr[32], port[7];
            if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY)
                strcpy(addr, "*");
            else
                strcpy(addr, inet_ntoa(sin->sin_addr));
            strcat(addr, ".");
            if (ntohs(sin->sin_port) == 0)
                strcpy(port, "*");
            else
                sprintf(port, "%d", ntohs(sin->sin_port));
            strcat(addr, port);
#endif
            return (addr);
        }

    const char *socket_modes[] = {
        "MUD",
        "STREAM",
        "DATAGRAM",
        "STREAM_BINARY",
        "DATAGRAM_BINARY"
    };

    const char *socket_states[] = {
        "CLOSED",
        "CLOSING",
        "UNBOUND",
        "BOUND",
        "LISTEN",
        "DATA_XFER"
    };

    /*
     * Return an array containing info for a socket
     */
    array_t *socket_status (int which)
    {
        array_t *ret;

        if (which < 0 || which >= max_lpc_socks) return 0;

        ret = allocate_empty_array(6);

        ret->item[0].type = T_NUMBER;
        ret->item[0].subtype = 0;
        ret->item[0].u.number = lpc_socks[which].fd;

        ret->item[1].type = T_STRING;
        ret->item[1].subtype = STRING_CONSTANT;
        ret->item[1].u.string = socket_states[lpc_socks[which].state];

        ret->item[2].type = T_STRING;
        ret->item[2].subtype = STRING_CONSTANT;
        ret->item[2].u.string = socket_modes[lpc_socks[which].mode];

        ret->item[3].type = T_STRING;
        ret->item[3].subtype = STRING_MALLOC;
        ret->item[3].u.string = string_copy(inet_address(&lpc_socks[which].l_addr),
                "socket_status");

        ret->item[4].type = T_STRING;
        ret->item[4].subtype = STRING_MALLOC;
        ret->item[4].u.string = string_copy(inet_address(&lpc_socks[which].r_addr),
                "socket_status");

        if (lpc_socks[which].owner_ob && !(lpc_socks[which].owner_ob->flags & O_DESTRUCTED)) {
            ret->item[5].type = T_OBJECT;
            ret->item[5].u.ob = lpc_socks[which].owner_ob;
            add_ref(lpc_socks[which].owner_ob, "socket_status");
        } else {
            ret->item[5] = const0u;
        }

        return ret;
    }

#endif  /* PACKAGE_SOCKETS */

#endif                          /* SOCKET_EFUNS */