1471 lines
41 KiB
C
1471 lines
41 KiB
C
|
|
/*
|
|
* 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;
|
|
}
|
|
#ifdef FD_CLOEXEC
|
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
#endif
|
|
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
|
|
memset(&sin, 0, sizeof(sin));
|
|
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
|
|
* better reset the close on exec as well then
|
|
*/
|
|
if (set_socket_nonblocking(accept_fd, 1) == -1) {
|
|
socket_perror("socket_accept: set_socket_nonblocking 1", 0);
|
|
OS_socket_close(accept_fd);
|
|
return EENONBLOCK;
|
|
}
|
|
|
|
#ifdef FD_CLOEXEC
|
|
fcntl(accept_fd, F_SETFD, FD_CLOEXEC);
|
|
#endif
|
|
|
|
|
|
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 = 1; //give the kernel some time to open the socket, but not too much!
|
|
#ifndef hpux
|
|
nb = select(accept_fd+1, (fd_set *) 0, &wmask, (fd_set *) 0, &t);
|
|
#else
|
|
nb = select(accept_fd+1, (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;
|
|
#ifndef AI_V4MAPPED
|
|
hints.ai_flags = AI_CANONNAME;
|
|
#else
|
|
hints.ai_flags = AI_CANONNAME| AI_V4MAPPED;
|
|
#endif
|
|
|
|
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].state != STATE_FLUSHING && 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 */
|