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. Steps: 1. Apply the diff 2. Replace the socket.c with the one provided below ----------------- Diff ------------------- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(); ----------------- End of Diff ------------------- ----------------- socket.c ------------------- /* * Sockets for ldmud 3.3 * * 5-July-2002 - Tomba @ Batmud (tomba@bat.org) * * First version * * 20-July-2002 - Tomba @ Batmud (tomba@bat.org) * * 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 * * 26-September-2002 - Tomba @ Batmud (tomba@bat.org) * * Sockets now send all the data in their buffer before actually * closing * * Some minor fixes * */ #include #include #include #include #include #include #include #include #include #include #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 #define SE_ILLEGAL_SOCKET_STATE -16 #define SE_ALREADYBOUND -17 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", "Illegal socket state", "Socket is already bound" }; enum socket_state { STATE_UNUSED = 0, STATE_ALLOCATED, STATE_READY, STATE_LISTENING, STATE_CONNECTING, STATE_CONNECTED, STATE_CLOSING }; static char* socket_state_strs[] = { "unused", "allocated", "ready", "listening", "connecting", "connected", "closing" }; struct socket_struct { int fd; int lpc_fd; /* "fd" shown to lpc */ enum socket_state state; object_t* object; int bound; 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->state = STATE_ALLOCATED; sock->object = 0; sock->bound = 0; memset(&sock->local_addr, 0, sizeof(sock->local_addr)); memset(&sock->remote_addr, 0, sizeof(sock->remote_addr)); put_number(&sock->read_callback, 0); put_number(&sock->close_callback, 0); put_number(&sock->connect_callback, 0); put_number(&sock->listen_callback, 0); sock->send_buffer = 0; sock->send_buffer_size = 0; sock->send_buffer_used = 0; return sock; } } return 0; } void free_socket_entry(socket_t* sock) { if(sock != 0) { // printf("Freeing fd %d, lpcfd %d\n", sock->fd, sock->lpc_fd); if(sock->fd != -1) { if(close(sock->fd) == -1) { perror("free_socket_entry: close"); } sock->fd = -1; } sock->state = STATE_UNUSED; sock->object = 0; free_svalue(&sock->read_callback); free_svalue(&sock->close_callback); free_svalue(&sock->connect_callback); free_svalue(&sock->listen_callback); put_number(&sock->read_callback, 0); put_number(&sock->close_callback, 0); put_number(&sock->connect_callback, 0); put_number(&sock->listen_callback, 0); if(sock->send_buffer) { free(sock->send_buffer); sock->send_buffer = 0; sock->send_buffer_size = 0; sock->send_buffer_used = 0; } 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 %s, buf %d, ob %s\n", i, sock->fd, socket_state_strs[sock->state], sock->send_buffer ? sock->send_buffer_used : 0, get_txt(sock->object->name)); } } return sp; } int socket_init_socket(socket_t* sock, int fd, svalue_t* close_callback, object_t* object) { 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 = 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], current_object); 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 lpc_fd = (sp--)->u.number; socket_t* sock = get_socket_entry(lpc_fd); if(sock == 0) { push_number(sp, SE_BADFD); return sp; } if(shutdown(sock->fd, 0) == -1) { fprintf(stderr, "socket_close: shutdown failed: %s\n", strerror(errno)); } sock->state = STATE_CLOSING; 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(sock->state != STATE_READY) { free_svalue(sp--); free_svalue(sp--); free_svalue(sp--); free_svalue(sp--); push_number(sp, SE_ILLEGAL_SOCKET_STATE); 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]); sp--; 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(sock->state != STATE_READY) { push_number(sp, SE_ILLEGAL_SOCKET_STATE); return sp; } if(sock->bound) { push_number(sp, SE_ALREADYBOUND); 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->bound = 1; 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; } if(sock->state != STATE_READY || !sock->bound) { free_svalue(sp--); free_svalue(sp--); push_number(sp, SE_ILLEGAL_SOCKET_STATE); return sp; } if(listen(sock->fd, 10) == -1) { myerrno = errno; perror("socket_listen: listen"); free_svalue(sp--); free_svalue(sp--); push_number(sp, socket_conv_errno(myerrno)); return sp; } assign_svalue(&sock->listen_callback, &sp[0]); free_svalue(sp--); free_svalue(sp--); sock->state = STATE_LISTENING; 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; } if(sock->state != STATE_LISTENING) { free_svalue(sp--); free_svalue(sp--); free_svalue(sp--); push_number(sp, SE_ILLEGAL_SOCKET_STATE); 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], current_object); 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; } if(sock->state != STATE_CONNECTED) { 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->state == STATE_CLOSING) && sock->send_buffer_used > 0) { int len, myerrno; len = send(sock->fd, sock->send_buffer, sock->send_buffer_used, 0); myerrno = errno; 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; } if(sock->state != STATE_CONNECTED) { free_svalue(sp--); free_svalue(sp--); push_number(sp, SE_ILLEGAL_SOCKET_STATE); 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_send(): 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 socket is closing and we have sent all the stuff out, free it */ if(sock->state == STATE_CLOSING && sock->send_buffer == 0) { 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 & POLLNVAL) { // printf("NVAL %d\n", sock->lpc_fd); push_number(inter_sp, sock->lpc_fd); push_number(inter_sp, SE_UNKNOWN); secure_callback_lambda(&sock->close_callback, 2); free_socket_entry(sock); return; } 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); } else { push_number(inter_sp, sock->lpc_fd); push_number(inter_sp, socket_conv_errno(err)); secure_callback_lambda(&sock->close_callback, 2); 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); } else { push_number(inter_sp, sock->lpc_fd); push_number(inter_sp, err); secure_callback_lambda(&sock->close_callback, 2); 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 if(sock->state == STATE_CONNECTED || sock->state == STATE_CLOSING) { socket_flush_send_buffer(sock); } else { fprintf(stderr, "Illegal socket state in POLLOUT. fd: %d, state %d\n", sock->fd, sock->state); } } 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); } 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_LISTENING) { push_number(inter_sp, sock->lpc_fd); secure_callback_lambda(&sock->listen_callback, 1); } else if(sock->state == STATE_CLOSING) { } else { fprintf(stderr, "socket_poll: read event in unknown state. fd: %d, state %d\n", sock->fd, sock->state); } } } ----------------- End of socket.c -------------------