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();