Short: TCP sockets for LDMud
From: Tomi Valkeinen
Date: 2002-09-12
Type: Patch
State: New
Driver: 3.2.9-dev.248
See also: f-981226-1, f-991104-0, p-021021
UDP is not handled, hardly documented, modelled after MudOS, but stable.
diff -burN 3-3/doc/efun/socket_accept 3-3.sock/doc/efun/socket_accept
--- 3-3/doc/efun/socket_accept Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_accept Thu Sep 12 14:16:34 2002
@@ -0,0 +1,9 @@
+SYNOPSIS
+ int socket_accept(int fd, closure read_callback,
+ closure close_callback)
+
+DESCRIPTION
+ Returns SE_SUCCESS or the error code.
+
+SEE ALSO
+
diff -burN 3-3/doc/efun/socket_address 3-3.sock/doc/efun/socket_address
--- 3-3/doc/efun/socket_address Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_address Thu Sep 12 14:17:47 2002
@@ -0,0 +1,8 @@
+SYNOPSIS
+ string socket_send(int fd)
+
+DESCRIPTION
+ Returns the remote address of the socket.
+
+SEE ALSO
+
diff -burN 3-3/doc/efun/socket_bind 3-3.sock/doc/efun/socket_bind
--- 3-3/doc/efun/socket_bind Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_bind Thu Sep 12 14:13:20 2002
@@ -0,0 +1,8 @@
+SYNOPSIS
+ int socket_bind(int fd, int port)
+
+DESCRIPTION
+ Returns SE_SUCCESS or the error code.
+
+SEE ALSO
+
diff -burN 3-3/doc/efun/socket_close 3-3.sock/doc/efun/socket_close
--- 3-3/doc/efun/socket_close Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_close Thu Sep 12 14:12:01 2002
@@ -0,0 +1,8 @@
+SYNOPSIS
+ int socket_close(int fd)
+
+DESCRIPTION
+ Returns SE_SUCCESS or the error code.
+
+SEE ALSO
+
diff -burN 3-3/doc/efun/socket_connect 3-3.sock/doc/efun/socket_connect
--- 3-3/doc/efun/socket_connect Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_connect Thu Sep 12 14:14:16 2002
@@ -0,0 +1,10 @@
+SYNOPSIS
+ int socket_connect(int fd, string address, closure connect_callback,
+ closure read_callback)
+
+DESCRIPTION
+ The address is in the form "ipnumber:port".
+ Returns SE_SUCCESS or the error code.
+
+SEE ALSO
+
diff -burN 3-3/doc/efun/socket_create 3-3.sock/doc/efun/socket_create
--- 3-3/doc/efun/socket_create Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_create Thu Sep 12 14:11:18 2002
@@ -0,0 +1,9 @@
+SYNOPSIS
+ int socket_create(closure close_callback)
+
+DESCRIPTION
+ Returns fd (zero or positive) for the newly created socket or
+ error number (negative) if the creation failed.
+
+SEE ALSO
+
diff -burN 3-3/doc/efun/socket_listen 3-3.sock/doc/efun/socket_listen
--- 3-3/doc/efun/socket_listen Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_listen Thu Sep 12 14:14:41 2002
@@ -0,0 +1,8 @@
+SYNOPSIS
+ int socket_listen(int fd, closure listen_callback)
+
+DESCRIPTION
+ Returns SE_SUCCESS or the error code.
+
+SEE ALSO
+
diff -burN 3-3/doc/efun/socket_send 3-3.sock/doc/efun/socket_send
--- 3-3/doc/efun/socket_send Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_send Thu Sep 12 14:17:17 2002
@@ -0,0 +1,8 @@
+SYNOPSIS
+ int socket_send(int fd, string data)
+
+DESCRIPTION
+ Returns SE_SUCCESS or the error code.
+
+SEE ALSO
+
diff -burN 3-3/doc/efun/socket_strerror 3-3.sock/doc/efun/socket_strerror
--- 3-3/doc/efun/socket_strerror Thu Jan 1 02:00:00 1970
+++ 3-3.sock/doc/efun/socket_strerror Thu Sep 12 14:18:16 2002
@@ -0,0 +1,8 @@
+SYNOPSIS
+ string socket_strerror(int errno)
+
+DESCRIPTION
+ Returns string representation of the given socket error.
+
+SEE ALSO
+
diff -burN 3-3/mudlib/socket1.c 3-3.sock/mudlib/socket1.c
--- 3-3/mudlib/socket1.c Thu Jan 1 02:00:00 1970
+++ 3-3.sock/mudlib/socket1.c Thu Sep 12 14:20:56 2002
@@ -0,0 +1,28 @@
+#include "sys/sockets.h"
+
+close_callback(int fd)
+{
+ debug_message(sprintf("close cb %d\n", fd));
+}
+
+read_callback(int fd, string msg)
+{
+ debug_message(sprintf("read cb %d, size %d: '%s'\n", fd, sizeof(msg), msg));
+}
+
+connect_callback(int fd, int err)
+{
+ debug_message(sprintf("connect cb %d, %s\n", fd, socket_strerror(err)));
+
+ if(err == SE_SUCCESS)
+ {
+ socket_send(fd, "hello world\n");
+ }
+}
+
+test()
+{
+ int fd = socket_create( (: close_callback($1) :) );
+
+ socket_connect(fd, "127.0.0.1:9999", #'connect_callback, #'read_callback);
+}
diff -burN 3-3/mudlib/socket2.c 3-3.sock/mudlib/socket2.c
--- 3-3/mudlib/socket2.c Thu Jan 1 02:00:00 1970
+++ 3-3.sock/mudlib/socket2.c Thu Sep 12 14:21:00 2002
@@ -0,0 +1,26 @@
+
+
+close_callback(int fd)
+{
+ debug_message(sprintf("close cb %d\n", fd));
+}
+
+read_callback(int fd, string msg)
+{
+ debug_message(sprintf("read cb %d, size %d: '%s'\n", fd, sizeof(msg), msg));
+}
+
+listen_callback(int fd)
+{
+ printf("listen cb %O %O\n", previous_object(), this_object());
+
+ socket_accept(fd, #'read_callback, #'close_callback);
+}
+
+test()
+{
+ int fd = socket_create( #'close_callback );
+
+ socket_bind(fd, 8888);
+ socket_listen(fd, #'listen_callback );
+}
diff -burN 3-3/mudlib/sys/sockets.h 3-3.sock/mudlib/sys/sockets.h
--- 3-3/mudlib/sys/sockets.h Thu Jan 1 02:00:00 1970
+++ 3-3.sock/mudlib/sys/sockets.h Thu Sep 12 14:05:11 2002
@@ -0,0 +1,21 @@
+#ifndef LPC_SOCKETS_H_
+#define LPC_SOCKETS_H_ 1
+
+#define SE_SUCCESS 0
+#define SE_UNKNOWN -1
+#define SE_CONNREFUSED -2
+#define SE_HOSTDOWN -3
+#define SE_HOSTUNREACH -4
+#define SE_NOMORESOCKETS -5
+#define SE_CREATESOCKET -6
+#define SE_SETNONBLOCKING -7
+#define SE_SETSOCKOPT -8
+#define SE_BADFD -9
+#define SE_ILLEGALADDR -10
+#define SE_INVALIDPORT -11
+#define SE_PIPE -12
+#define SE_ADDRINUSE -13
+#define SE_INVAL -14
+#define SE_CONNRESET -15
+
+#endif /* LPC_SOCKETS_H_ */
diff -burN 3-3/mudlib/test_master.c 3-3.sock/mudlib/test_master.c
--- 3-3/mudlib/test_master.c Fri Aug 16 06:03:45 2002
+++ 3-3.sock/mudlib/test_master.c Thu Sep 12 14:19:20 2002
@@ -47,6 +47,18 @@
return;
}
+ if (arg == "socket1")
+ {
+ load_object("socket1")->test();
+ return;
+ }
+
+ if (arg == "socket2")
+ {
+ load_object("socket2")->test();
+ return;
+ }
+
if (arg == "gc")
{
garbage_collection();
diff -burN 3-3/src/Makefile.in 3-3.sock/src/Makefile.in
--- 3-3/src/Makefile.in Wed Aug 21 21:07:36 2002
+++ 3-3.sock/src/Makefile.in Thu Sep 12 13:36:18 2002
@@ -77,7 +77,7 @@
parser.c parse.c parse_old.c pkg-alists.c pkg-mysql.c pkg-pcre.c port.c \
ptrtable.c \
random.c regexp.c simulate.c simul_efun.c stdstrings.c \
- strfuns.c sprintf.c swap.c wiz_list.c xalloc.c
+ strfuns.c sprintf.c swap.c wiz_list.c xalloc.c sockets.c
OBJ = access_check.o actions.o array.o backend.o bitstrings.o call_out.o \
closure.o comm.o \
dumpstat.o ed.o efuns.o files.o gcollect.o hash.o heartbeat.o \
@@ -87,7 +87,7 @@
parser.o parse.o parse_old.o pkg-alists.o pkg-mysql.o pkg-pcre.o port.o \
ptrtable.o \
random.o regexp.o simulate.o simul_efun.o stdstrings.o \
- strfuns.o sprintf.o swap.o wiz_list.o xalloc.o @ALLOCA@
+ strfuns.o sprintf.o swap.o wiz_list.o xalloc.o sockets.o @ALLOCA@
driver@EXEEXT@: $(OBJ)
$(CC) @OPTIMIZE_LINKING@ $(LDFLAGS) $(OBJ) -o $@ $(LIBS)
diff -burN 3-3/src/backend.c 3-3.sock/src/backend.c
--- 3-3/src/backend.c Wed Aug 21 21:07:36 2002
+++ 3-3.sock/src/backend.c Thu Sep 12 13:54:29 2002
@@ -71,6 +71,7 @@
#include "swap.h"
#include "wiz_list.h"
#include "xalloc.h"
+#include "sockets.h"
#include "../mudlib/sys/driver_hook.h"
#include "../mudlib/sys/debug_message.h"
@@ -646,6 +647,8 @@
wiz_decay();
}
+ socket_poll();
+
} /* end of main loop */
/* NOTREACHED */
diff -burN 3-3/src/func_spec 3-3.sock/src/func_spec
--- 3-3/src/func_spec Fri Sep 6 10:00:07 2002
+++ 3-3.sock/src/func_spec Thu Sep 12 13:37:54 2002
@@ -643,4 +643,15 @@
#endif /* USE_MYSQL */
+string socket_strerror(int);
+void socket_print_stats();
+int socket_create(closure);
+int socket_close(int);
+int socket_connect(int, string, closure, closure);
+int socket_bind(int, int);
+int socket_listen(int, closure);
+int socket_accept(int, closure, closure);
+int socket_send(int, string);
+string socket_address(int);
+
/***************************************************************************/
diff -burN 3-3/src/main.c 3-3.sock/src/main.c
--- 3-3/src/main.c Thu Sep 5 00:02:49 2002
+++ 3-3.sock/src/main.c Thu Sep 12 13:55:23 2002
@@ -68,6 +68,7 @@
#include "swap.h"
#include "wiz_list.h"
#include "xalloc.h"
+#include "sockets.h"
/*-------------------------------------------------------------------------*/
@@ -441,6 +442,8 @@
#endif /* ERQ_DEMON */
initialize_host_ip_number();
+ socket_init();
+
(void)signal(SIGFPE, SIG_IGN);
current_object = &dummy_current_object_for_loads;
if (setjmp(toplevel_context.con.text)) {
diff -burN 3-3/src/simulate.c 3-3.sock/src/simulate.c
--- 3-3/src/simulate.c Wed Sep 4 21:38:36 2002
+++ 3-3.sock/src/simulate.c Thu Sep 12 13:56:40 2002
@@ -2223,6 +2223,8 @@
if (ob->flags & O_DESTRUCTED)
return;
+ socket_object_destructed(ob);
+
#ifdef CHECK_OBJECT_REF
xallocate(shadow, sizeof(*shadow), "destructed object shadow");
#endif /* CHECK_OBJECT_REF */
diff -burN 3-3/src/sockets.c 3-3.sock/src/sockets.c
--- 3-3/src/sockets.c Thu Jan 1 02:00:00 1970
+++ 3-3.sock/src/sockets.c Thu Sep 12 13:53:38 2002
@@ -0,0 +1,1027 @@
+/*
+ * Sockets for ldmud 3.3
+ *
+ * 5-July-2002 - Tomba @ Batmud
+ * * First version
+ *
+ * 20-July-2002 - Tomba @ Batmud
+ * * Added ECONNRESET
+ * * Fixed socket_address to return empty string on error
+ * * sock->send_buffer was not always cleared when freed
+ * * removed a few debug printfs
+ *
+ * TODO: UDP support
+ * TODO: access right handling
+ * TODO: event handling with select (comm.c) instead of poll
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+
+#include "main.h"
+#include "svalue.h"
+#include "interpret.h"
+#include "simulate.h"
+#include "object.h"
+#include "mstrings.h"
+#include "comm.h"
+#include "actions.h"
+
+#define SE_SUCCESS 0
+#define SE_UNKNOWN -1
+#define SE_CONNREFUSED -2
+#define SE_HOSTDOWN -3
+#define SE_HOSTUNREACH -4
+#define SE_NOMORESOCKETS -5
+#define SE_CREATESOCKET -6
+#define SE_SETNONBLOCKING -7
+#define SE_SETSOCKOPT -8
+#define SE_BADFD -9
+#define SE_ILLEGALADDR -10
+#define SE_INVALIDPORT -11
+#define SE_PIPE -12
+#define SE_ADDRINUSE -13
+#define SE_INVAL -14
+#define SE_CONNRESET -15
+
+static char *socket_errors[] = {
+ "Success",
+ "Unknown error",
+ "Connection refused",
+ "Host is down",
+ "No route to host",
+ "No more mud sockets available",
+ "Failed to create socket",
+ "Failed to set socket to non-blocking mode",
+ "Failed to set socket options",
+ "Bad socket descriptor",
+ "Illegal address",
+ "Invalid port",
+ "Broken pipe",
+ "Address already in use",
+ "Invalid argument",
+ "Connection reset by peer"
+};
+
+
+enum socket_state
+{
+ STATE_UNUSED = 0,
+ STATE_READY,
+ STATE_BOUND,
+ STATE_LISTEN,
+ STATE_CONNECTING,
+ STATE_CONNECTED,
+ STATE_CLOSING
+};
+
+struct socket_struct
+{
+ int fd;
+ int lpc_fd; /* "fd" shown to lpc */
+ enum socket_state state;
+ object_t* object;
+ struct sockaddr_in local_addr;
+ struct sockaddr_in remote_addr;
+ svalue_t read_callback;
+ svalue_t close_callback;
+ svalue_t connect_callback;
+ svalue_t listen_callback;
+ char* send_buffer;
+ int send_buffer_size;
+ int send_buffer_used;
+} typedef socket_t;
+
+
+#define MAX_LPC_FD 100
+
+static socket_t g_socket_table[MAX_LPC_FD];
+static struct pollfd g_poll_table[MAX_LPC_FD];
+
+#define READ_BUFFER_SIZE 10240
+static char g_read_buffer[READ_BUFFER_SIZE];
+
+void socket_process_events(socket_t* sock, short events);
+
+
+
+void socket_init()
+{
+ int i;
+ for(i = 0; i < MAX_LPC_FD; i++)
+ {
+ struct socket_struct* sock = &g_socket_table[i];
+
+ sock->state = STATE_UNUSED;
+ sock->lpc_fd = i;
+
+ g_poll_table[i].fd = -1;
+ g_poll_table[i].events = POLLIN | POLLOUT;
+ g_poll_table[i].revents = 0;
+
+ }
+}
+
+int socket_conv_errno(int err)
+{
+ switch(err)
+ {
+ case ECONNREFUSED:
+ return SE_CONNREFUSED;
+
+ case EHOSTDOWN:
+ return SE_HOSTDOWN;
+
+ case EHOSTUNREACH:
+ return SE_HOSTUNREACH;
+
+ case EPIPE:
+ return SE_PIPE;
+
+ case EADDRINUSE:
+ return SE_ADDRINUSE;
+
+ case EINVAL:
+ return SE_INVAL;
+
+ case ECONNRESET:
+ return SE_CONNRESET;
+
+ default:
+ fprintf(stderr, "socket_conv_errno: unknown system error %d, %s\n", err, strerror(err));
+ return SE_UNKNOWN;
+ }
+}
+
+struct socket_struct* new_socket_entry()
+{
+ int i;
+
+ for (i = 0; i < MAX_LPC_FD; i++)
+ {
+ struct socket_struct* sock = &g_socket_table[i];
+
+ if (sock->state == STATE_UNUSED)
+ {
+ sock->fd = -1;
+ sock->send_buffer = 0;
+ sock->send_buffer_size = 0;
+ sock->send_buffer_used = 0;
+
+ put_number(&sock->read_callback, 0);
+ put_number(&sock->close_callback, 0);
+ put_number(&sock->connect_callback, 0);
+ put_number(&sock->listen_callback, 0);
+
+ return sock;
+ }
+ }
+
+ return 0;
+}
+
+void free_socket_entry(socket_t* sock)
+{
+ if(sock != 0)
+ {
+ if(sock->fd != -1)
+ {
+ if(close(sock->fd) == -1)
+ {
+ perror("free_socket_entry: close");
+ }
+
+ sock->fd = -1;
+ }
+
+ if(sock->send_buffer)
+ {
+ free(sock->send_buffer);
+ sock->send_buffer = 0;
+ }
+
+ sock->state = STATE_UNUSED;
+
+ free_svalue(&sock->read_callback);
+ free_svalue(&sock->close_callback);
+ free_svalue(&sock->connect_callback);
+ free_svalue(&sock->listen_callback);
+
+ g_poll_table[sock->lpc_fd].fd = -1;
+ }
+}
+
+socket_t* get_socket_entry(int lpc_fd)
+{
+ if(lpc_fd < 0 || lpc_fd >= MAX_LPC_FD ||
+ g_socket_table[lpc_fd].state == STATE_UNUSED)
+ {
+ return 0;
+ }
+
+ return &g_socket_table[lpc_fd];
+}
+
+void socket_object_destructed(object_t* ob)
+{
+ int i;
+
+ for(i = 0; i < MAX_LPC_FD; i++)
+ {
+ struct socket_struct* sock = &g_socket_table[i];
+
+ if(sock->state != STATE_UNUSED)
+ {
+ if(sock->object == ob)
+ {
+ free_socket_entry(sock);
+ }
+ }
+ }
+}
+
+
+
+
+
+
+svalue_t* f_socket_strerror(svalue_t* sp)
+{
+ int err = 0 - sp->u.number;
+ sp--;
+
+ if(err < 0 || err > sizeof(socket_errors))
+ {
+ push_string(sp, new_mstring("Unspecified error"));
+ }
+ else
+ {
+ push_string(sp, new_mstring(socket_errors[err]));
+ }
+
+ return sp;
+}
+
+svalue_t* f_socket_print_stats(svalue_t* sp)
+{
+ int i;
+
+ add_message("-- Socket stats --\n");
+
+ for(i = 0; i < MAX_LPC_FD; i++)
+ {
+ socket_t* sock = &g_socket_table[i];
+
+ if(sock->state != STATE_UNUSED)
+ {
+ add_message("lpc_fd %d, fd %d, state %d, ob %s\n",
+ i, sock->fd, sock->state, get_txt(sock->object->name));
+ }
+ }
+
+ return sp;
+}
+
+int socket_init_socket(socket_t* sock, int fd, svalue_t* close_callback)
+{
+ int tmp;
+
+ if(fcntl(fd, F_SETFL, O_NDELAY) == -1)
+ {
+ perror("socket_init_socket: fcntl O_NDELAY");
+
+ return SE_SETNONBLOCKING;
+ }
+
+ tmp = 1;
+ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&tmp, sizeof(tmp)) == -1)
+ {
+ perror("socket_init_socket: setsockopt SO_REUSEADDR");
+
+ return SE_SETSOCKOPT;
+ }
+
+ sock->fd = fd;
+ sock->state = STATE_READY;
+ sock->object = current_object;
+
+ assign_svalue(&sock->close_callback, close_callback);
+
+ g_poll_table[sock->lpc_fd].fd = sock->fd;
+
+ return 0;
+}
+
+int socket_string_to_sockaddr(char* name, struct sockaddr_in* saddr)
+{
+#define ADDR_BUF_SIZE 512
+ int port;
+ char *cp, addr[ADDR_BUF_SIZE];
+
+ strncpy(addr, name, ADDR_BUF_SIZE);
+
+ cp = strchr(addr, ':');
+ if (cp == NULL)
+ {
+ return 0;
+ }
+
+ *cp = '\0';
+ port = atoi(cp + 1);
+
+ saddr->sin_family = AF_INET;
+ saddr->sin_port = htons((unsigned short)port);
+ if(inet_aton(addr, &saddr->sin_addr) == 0)
+ {
+ return 0;
+ }
+
+ return 1;
+#undef ADDR_BUF_SIZE
+}
+
+
+
+svalue_t* f_socket_create(svalue_t* sp)
+{
+ int err, fd;
+ socket_t* sock;
+
+ if(current_object->flags & O_DESTRUCTED)
+ {
+ error("socket_create: this_object has been destructed");
+ }
+
+ sock = new_socket_entry();
+
+ if(sock == 0)
+ {
+ free_svalue(sp--);
+ push_number(sp, SE_NOMORESOCKETS);
+ return sp;
+ }
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+
+ if(fd == -1)
+ {
+ perror("socket_create: socket");
+
+ free_socket_entry(sock);
+
+ free_svalue(sp--);
+
+ push_number(sp, SE_CREATESOCKET);
+ return sp;
+ }
+
+ err = socket_init_socket(sock, fd, &sp[0]);
+
+ free_svalue(sp--);
+
+ if(err != 0)
+ {
+ close(fd);
+
+ free_socket_entry(sock);
+
+ push_number(sp, err);
+
+ return sp;
+ }
+
+ push_number(sp, sock->lpc_fd);
+
+ return sp;
+}
+
+svalue_t* f_socket_close(svalue_t* sp)
+{
+ int ret, myerrno;
+
+ int lpc_fd = (sp--)->u.number;
+
+ socket_t* sock = get_socket_entry(lpc_fd);
+
+ if(sock == 0)
+ {
+ push_number(sp, SE_BADFD);
+ return sp;
+ }
+
+ sock->state = STATE_CLOSING;
+
+ ret = close(sock->fd);
+ myerrno = errno;
+ sock->fd = -1;
+
+ if(ret == -1)
+ {
+ perror("socket_close: close");
+
+ push_number(sp, socket_conv_errno(myerrno));
+ }
+ else
+ {
+ push_number(sp, SE_SUCCESS);
+ }
+
+ return sp;
+}
+
+svalue_t* f_socket_connect(svalue_t* sp)
+{
+ int lpc_fd, myerrno;
+ int ret;
+ socket_t* sock;
+
+ lpc_fd = sp[-3].u.number;
+
+ sock = get_socket_entry(lpc_fd);
+ if(sock == 0)
+ {
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, SE_BADFD);
+ return sp;
+ }
+
+ if(socket_string_to_sockaddr(get_txt(sp[-2].u.str), &sock->remote_addr) == 0)
+ {
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, SE_ILLEGALADDR);
+ return sp;
+ }
+
+ ret = connect(sock->fd, (struct sockaddr *)&sock->remote_addr,
+ sizeof(sock->remote_addr));
+ myerrno = errno;
+
+ if(ret == -1 && myerrno != EINPROGRESS)
+ {
+ fprintf(stderr, "socket_connect: connect failed: %s\n", strerror(myerrno));
+
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, socket_conv_errno(myerrno));
+ }
+ else
+ {
+ sock->state = STATE_CONNECTING;
+
+ transfer_svalue(&sock->connect_callback, &sp[-1]);
+ transfer_svalue(&sock->read_callback, &sp[0]);
+
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, SE_SUCCESS);
+ }
+
+ return sp;
+}
+
+svalue_t* f_socket_bind(svalue_t* sp)
+{
+ socket_t* sock;
+ int myerrno;
+
+ int port = (sp--)->u.number;
+ int lpc_fd = (sp--)->u.number;
+
+ sock = get_socket_entry(lpc_fd);
+ if(sock == 0)
+ {
+ push_number(sp, SE_BADFD);
+ return sp;
+ }
+
+ if(port < 0)
+ {
+ push_number(sp, SE_INVALIDPORT);
+ return sp;
+ }
+
+ sock->local_addr.sin_port = htons((unsigned short)port);
+ sock->local_addr.sin_addr.s_addr = INADDR_ANY;
+ sock->local_addr.sin_family = AF_INET;
+
+ if(bind(sock->fd, (struct sockaddr*)&sock->local_addr,
+ sizeof(sock->local_addr)) == -1)
+ {
+ myerrno = errno;
+
+ perror("socket_bind: bind");
+ push_number(sp, socket_conv_errno(myerrno));
+ }
+ else
+ {
+ sock->state = STATE_BOUND;
+ push_number(sp, SE_SUCCESS);
+ }
+
+ return sp;
+}
+
+svalue_t* f_socket_listen(svalue_t* sp)
+{
+ socket_t* sock;
+ int myerrno;
+
+ int lpc_fd = sp[-1].u.number;
+
+ sock = get_socket_entry(lpc_fd);
+ if(sock == 0)
+ {
+ free_svalue(sp--);
+ free_svalue(sp--);
+ push_number(sp, SE_BADFD);
+ return sp;
+ }
+
+ transfer_svalue(&sock->listen_callback, &sp[0]);
+
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ if(listen(sock->fd, 10) == -1)
+ {
+ myerrno = errno;
+ perror("socket_listen: listen");
+ push_number(sp, socket_conv_errno(myerrno));
+ }
+ else
+ {
+ sock->state = STATE_LISTEN;
+ push_number(sp, SE_SUCCESS);
+ }
+
+ return sp;
+}
+
+svalue_t* f_socket_accept(svalue_t* sp)
+{
+ socket_t* sock;
+ socket_t* new_sock;
+ int new_fd;
+ struct sockaddr_in addr;
+ int addr_size = sizeof(addr);
+ int err;
+ int myerrno;
+
+ int lpc_fd = sp[-2].u.number;
+
+ sock = get_socket_entry(lpc_fd);
+ if(sock == 0)
+ {
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, SE_BADFD);
+ return sp;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr*)&addr, &addr_size);
+ myerrno = errno;
+ if(new_fd < 0)
+ {
+ perror("socket_accept: accept");
+
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, socket_conv_errno(myerrno));
+ return sp;
+ }
+
+ new_sock = new_socket_entry();
+
+ if(new_sock == 0)
+ {
+ close(new_fd);
+
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, SE_NOMORESOCKETS);
+ return sp;
+ }
+
+ err = socket_init_socket(new_sock, new_fd, &sp[0]);
+
+ if(err != 0)
+ {
+ close(new_fd);
+
+ free_socket_entry(new_sock);
+
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, err);
+ }
+
+ assign_svalue(&new_sock->read_callback, &sp[-1]);
+
+ memcpy(&new_sock->remote_addr, &addr, sizeof(addr));
+
+ free_svalue(sp--);
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ new_sock->state = STATE_CONNECTED;
+
+ push_number(sp, new_sock->lpc_fd);
+
+ return sp;
+}
+
+svalue_t* f_socket_address(svalue_t* sp)
+{
+#define ADDR_BUF_SIZE 512
+ char addrstr[ADDR_BUF_SIZE];
+ socket_t* sock;
+
+ int lpc_fd = sp[0].u.number;
+
+ sock = get_socket_entry(lpc_fd);
+
+ free_svalue(sp--);
+
+ if(sock == 0)
+ {
+ push_string(sp, new_mstring(""));
+ return sp;
+ }
+
+ snprintf(addrstr, 511, "%s:%d",
+ inet_ntoa(sock->remote_addr.sin_addr),
+ ntohs(sock->remote_addr.sin_port));
+ addrstr[ADDR_BUF_SIZE-1] = 0;
+
+ push_string(sp, new_mstring(addrstr));
+
+ return sp;
+#undef ADDR_BUF_SIZE
+}
+
+void socket_flush_send_buffer(socket_t* sock)
+{
+ if(sock->state == STATE_CONNECTED && sock->send_buffer_used > 0)
+ {
+ int len, myerrno;
+
+ len = send(sock->fd, sock->send_buffer, sock->send_buffer_used, 0);
+ myerrno = errno;
+
+ if(myerrno == EAGAIN || myerrno == EWOULDBLOCK)
+ {
+ len = 0;
+ }
+
+ if(len == -1)
+ {
+ fprintf(stderr, "socket_flush_send_buffer: send failed %s\n", strerror(myerrno));
+
+ /* error -> clear buffer */
+ free(sock->send_buffer);
+ sock->send_buffer = 0;
+ sock->send_buffer_size = 0;
+ sock->send_buffer_used = 0;
+ }
+ else if(len < sock->send_buffer_used)
+ {
+ memmove(sock->send_buffer, sock->send_buffer + len,
+ sock->send_buffer_used - len);
+
+ sock->send_buffer_used = sock->send_buffer_used - len;
+/*
+ printf("socket: sent %d of %d. %d in buffer\n", len, sock->send_buffer_used+len,
+ sock->send_buffer_used);
+*/
+ }
+ else
+ {
+/*
+ printf("socket: sent %d of %d. %d in buffer\n", len, sock->send_buffer_used,
+ 0);
+*/
+ free(sock->send_buffer);
+ sock->send_buffer = 0;
+ sock->send_buffer_size = 0;
+ sock->send_buffer_used = 0;
+ }
+ }
+}
+
+svalue_t* f_socket_send(svalue_t* sp)
+{
+ int lpc_fd, len, myerrno;
+ socket_t* sock;
+ char* buf;
+ int buflen;
+
+ lpc_fd = sp[-1].u.number;
+
+ sock = get_socket_entry(lpc_fd);
+
+ if(sock == 0)
+ {
+ free_svalue(sp--);
+ free_svalue(sp--);
+ push_number(sp, SE_BADFD);
+ return sp;
+ }
+
+ buf = get_txt(sp[0].u.str);
+ buflen = mstrsize(sp[0].u.str);
+
+ socket_flush_send_buffer(sock);
+
+ if(sock->send_buffer_used == 0)
+ {
+ len = send(sock->fd, buf, buflen, 0);
+ myerrno = errno;
+ }
+ else
+ {
+ myerrno = 0;
+ len = 0;
+ }
+
+ if(len == -1 && (myerrno == EAGAIN || myerrno == EWOULDBLOCK))
+ {
+ len = 0;
+ }
+
+ if(len == -1)
+ {
+ fprintf(stderr, "socket_send: send failed %s\n", strerror(myerrno));
+
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, socket_conv_errno(myerrno));
+ }
+ else if(len < buflen)
+ {
+ if(buflen - len > sock->send_buffer_size - sock->send_buffer_used)
+ {
+ sock->send_buffer = realloc(sock->send_buffer,
+ sock->send_buffer_used + buflen - len);
+
+ if(!sock->send_buffer)
+ {
+ error("out of memory");
+ }
+ else
+ {
+ sock->send_buffer_size = sock->send_buffer_used + buflen - len;
+ }
+ }
+
+ memcpy(sock->send_buffer + sock->send_buffer_used,
+ buf + len,
+ buflen - len);
+
+ sock->send_buffer_used += buflen - len;
+/*
+ printf("socket: sent %d of %d. %d in buffer\n", len, buflen, sock->send_buffer_used);
+*/
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, len);
+ }
+ else
+ {
+ free_svalue(sp--);
+ free_svalue(sp--);
+
+ push_number(sp, SE_SUCCESS);
+ }
+
+ return sp;
+}
+
+void socket_poll()
+{
+ int ret, i, myerrno;
+
+ object_t* hide_current_object = current_object;
+ program_t* hide_current_prog = current_prog;
+ object_t* hide_interactive = current_interactive;
+ object_t* hide_command_giver = command_giver;
+
+ current_interactive = 0;
+ command_giver = 0;
+
+ for(i = 0; i < MAX_LPC_FD; i++)
+ {
+ socket_t* sock = &g_socket_table[i];
+
+ if(sock->state == STATE_CLOSING)
+ {
+ free_socket_entry(sock);
+ }
+ }
+
+ ret = poll(g_poll_table, MAX_LPC_FD, 0);
+ myerrno = errno;
+
+ if(ret == -1)
+ {
+ perror("socket_poll: poll");
+ }
+ else
+ {
+ for(i = 0; i < MAX_LPC_FD; i++)
+ {
+ if(g_poll_table[i].revents)
+ {
+ socket_t* sock = get_socket_entry(i);
+
+ if(sock == 0)
+ {
+ error("Internal error in socket_poll()");
+ }
+
+ socket_process_events(sock, g_poll_table[i].revents);
+ }
+ }
+ }
+
+ current_interactive = hide_interactive;
+ command_giver = hide_command_giver;
+ current_object = hide_current_object;
+ current_prog = hide_current_prog;
+}
+
+
+void socket_process_events(socket_t* sock, short events)
+{
+ int err, errlen;
+ int myerrno;
+
+ if(events & POLLERR)
+ {
+// printf("ERR %d\n", sock->lpc_fd);
+
+ errlen = sizeof(err);
+ if(getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1)
+ {
+ perror("socket_process_events: getsockopt");
+ }
+
+ if(sock->state == STATE_CONNECTING)
+ {
+ push_number(inter_sp, sock->lpc_fd);
+ push_number(inter_sp, socket_conv_errno(err));
+ secure_callback_lambda(&sock->connect_callback, 2);
+ }
+
+ /* callback may have closed the socket */
+ if(sock->state != STATE_CLOSING)
+ {
+ push_number(inter_sp, sock->lpc_fd);
+ push_number(inter_sp, socket_conv_errno(err));
+
+ secure_callback_lambda(&sock->close_callback, 2);
+
+ /* callback may have closed the socket */
+ if(sock->state != STATE_CLOSING)
+ free_socket_entry(sock);
+ }
+
+ return;
+ }
+
+ if(events & POLLHUP)
+ {
+// printf("HUP %d\n", sock->lpc_fd);
+
+ errlen = sizeof(err);
+ if(getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1)
+ {
+ perror("socket_process_events: getsockopt");
+ }
+
+ if(sock->state == STATE_CONNECTING)
+ {
+ push_number(inter_sp, sock->lpc_fd);
+ push_number(inter_sp, err);
+ secure_callback_lambda(&sock->connect_callback, 2);
+ }
+
+ /* callback may have closed the socket */
+ if(sock->state != STATE_CLOSING)
+ {
+ push_number(inter_sp, sock->lpc_fd);
+ push_number(inter_sp, err);
+
+ secure_callback_lambda(&sock->close_callback, 2);
+
+ /* callback may have closed the socket */
+ if(sock->state != STATE_CLOSING)
+ free_socket_entry(sock);
+ }
+
+ return;
+ }
+
+ if(events & POLLOUT)
+ {
+// printf("OUT %d\n", sock->lpc_fd);
+
+ if(sock->state == STATE_CONNECTING)
+ {
+ push_number(inter_sp, sock->lpc_fd);
+ push_number(inter_sp, 0);
+ secure_callback_lambda(&sock->connect_callback, 2);
+
+ sock->state = STATE_CONNECTED;
+ }
+ else
+ {
+ socket_flush_send_buffer(sock);
+ }
+ }
+
+ if(events & POLLIN)
+ {
+// printf("IN %d\n", sock->lpc_fd);
+
+ if(sock->state == STATE_CONNECTED)
+ {
+ int len;
+
+ len = recv(sock->fd, &g_read_buffer, READ_BUFFER_SIZE, 0);
+ myerrno = errno;
+
+ if(len == -1)
+ {
+ perror("socket_process_events: recv");
+
+ push_number(inter_sp, sock->lpc_fd);
+ push_number(inter_sp, 0);
+ push_number(inter_sp, socket_conv_errno(myerrno));
+
+ secure_callback_lambda(&sock->read_callback, 3);
+ }
+ else if(len == 0)
+ {
+ push_number(inter_sp, sock->lpc_fd);
+ push_number(inter_sp, 0);
+
+ secure_callback_lambda(&sock->close_callback, 2);
+
+ free_socket_entry(sock);
+ return;
+ }
+ else
+ {
+ push_number(inter_sp, sock->lpc_fd);
+ push_string(inter_sp, mstring_new_n_string(g_read_buffer, len));
+ push_number(inter_sp, len);
+
+ secure_callback_lambda(&sock->read_callback, 3);
+ }
+ }
+ else if(sock->state == STATE_LISTEN)
+ {
+ push_number(inter_sp, sock->lpc_fd);
+
+ secure_callback_lambda(&sock->listen_callback, 1);
+ }
+ else
+ {
+ fprintf(stderr, "socket_poll: read event in unknown state %d\n", sock->state);
+ }
+ }
+}
diff -burN 3-3/src/sockets.h 3-3.sock/src/sockets.h
--- 3-3/src/sockets.h Thu Jan 1 02:00:00 1970
+++ 3-3.sock/src/sockets.h Thu Sep 12 13:35:17 2002
@@ -0,0 +1,6 @@
+
+void socket_init();
+
+void socket_object_destructed(object_t* ob);
+
+void socket_poll();