View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0000163 | LDMud | Networking | public | 2004-11-26 19:54 | 2004-11-26 20:05 |
Reporter | Assigned To | ||||
Priority | normal | Severity | feature | Reproducibility | N/A |
Status | new | Resolution | open | ||
Summary | 0000163: TCP Support | ||||
Description | Short: TCP Support Type: Feature State: Unclassified From: Marcus Meissner <marcus@jet.franken.de> From: Lars Date: Tue, 22 Dec 1998 11:57:41 +0100 (MET) See also: f-991104-0, p-020912, p-021021 See also: MudOS, Anarres version, regarding the telnet machine. Ich meine damit, erstmal genau ueberlegen, wie die API aussehen soll und nicht einfach die UNIX socket* functions nachbilden, sonder etwas LPC bezogenes basteln. Ich hab mal drueber nachgedacht, einige Punkte, die wir beachten muessen: - Wir muessen immer nonblocking arbeiten. -> Writes muessen wir wohl im Driver buffern, weil das zu kompliziert fuer LPC Benutzer ist. -> Reads sollten als Callbacks durchgegeben werden. -> accept/connect Behandlung braucht extra callbacks. - Kein neuer LPC Variablentyp, das wuerde nur den Driver komplizieren. Soweit ich mit Gedanken gekommen bin: - Sockets werden in LPC spezifiziert durch die lokale Portnummer (die ist fuer TCP unique auf dem Rechner, nur wird der Portnummer Adressraum von UDP ueberlappt...Sprich, man muesste UDP anders behandeln. Und UNIX Domain sockets, und IPX ... ) - Wer darf write ausfuehren? Nur der Master, der dann evt. dieses Privileg per closure/functionptr weitergibt? Oder sollten wir nen Socket an ein Objekt binden? -> Generell Privileg Vergabe abklaeren. Insgesamt hatte ich ungefaehre folgende API im Kopf entwickelt erstmal, die noch einige unklare Stellen hat: # Macht socket/bind/listen/ socket-> select auf readability tcp_accept(int localport,closure _accept_fun); // eventuell hostmask ? # Wenn der accept socket readable wird ... accept und dann diese Funktion # aufrufen. _accept_fun(int localportnr,string remotehost,int remoteport) { /* Problem: Wir muessen dem GD jetzt irgendwie sagen, wem dieser * socket gehoert und wo er readcallbacks aufrufen soll */ } # write(2) ... Mit buffering im driver. Schreibt immer soviel wie angegeben aus # LPC sicht, wirft NICHTS weg. void tcp_write(localport,int*|string); # close(2) ... sollte den rest des obigen writebuffers leeren und dann erst # den socket schliessen. Bzw. das durch evt. zusaetzliches Flag machen. void tcp_close(localport); # socket(2)/(connect(2) nonblocking). Ruft _connect_fun auf, wenn select(2) # Schreibbarkeit oder Lesbarkeit signalisiert. void tcp_connect(string remotehost,int remoteport,closure _connect_fun); # bei readable/writeable connect aufgerufen. (remotehost/remoteport evt. # unnoetig) _connect_fun(int localportnr,string remotehost,int remoteport) { /* sollte irgendwie jetzt auch dem GD sagen, wem der Socket gehoert * und wo er readcallbacks aufrufen soll */ } Dass Problem ist jetzt das spezifizieren von readcallbacks. Evt. kann man die closure als returnwert von _connect_fun oder _accept_fun zurueckgeben, oder noch eine API function verwenden, wie zB tcp_bind(int localportnr,closure _socket_callback); _socket_callback(int localport,int type,mixed stuff) { /*type: SOCKET_READ * stuff enthaelt entweder int* oder string (je nach intelligenter * binary detection. *type: SOCKET_ERROR * hmm *type: SOCKET_CLOSE * hmm */ } Ueber besonders das letztere bin ich mir noch nicht ganz klar. Vielleiocht sollte ich mir auch erstmal die MudOS implementation ansehen, bevor ich mir NIH vorwerfen lasse :) And lars wrote: > Sprich, es spricht kaum was gegen ans Objekte binden. > Ausser, wie genau man das nun machen sollte. Driverintern relativ einfach, da mit der interactive-sentence die notwendige Basisarchitektur vorhanden ist. > Problem gibts wohl nur bei den leidigen Referencecounter. ? > > > # write(2) ... Mit buffering im driver. Schreibt immer soviel wie > > > # angegeben aus LPC sicht, wirft NICHTS weg. > > > void tcp_write(localport,int*|string); > > > > Das wuerde ich socket_write() nennen, und zumindest einen Fehlercode > > zurueckgeben lassen. Prinzipiell koennte der Socket ja auch ein UDP- > > oder sonstwas Socket sein, 'tcp_' waere dann irrefuehrend. > > Hmm. Dann sollte man aber mit mehr als localport arbeiten, weil sich > die portnummerraeume von TCP und UDP ueberschneiden. > Oder man nimmt als Unique ID einen string mit "tcp:nr" oder "udp:nr". Es sit moeglich ueber einen Socket/Port gleichzeitig TCP und UDP zu machen (habs noch nie probiert und die TCP/IP-Buecher sind in der Bibliothek) - oder iaW: TCP und UDP sind keine Namensraeume an sich. Eine Unique-ID eruebrigt sich, da der Socket durch das Objekt definiert ist. Funktionen wie socket_write() wuerden dann entweder ein Socketobjekt als erstes Argument erwarten, oder nur funktionieren wenn this_object() ein Socketobject ist. query_mud_port(socket_object) wuerde die Portnummer liefern, query_socket_type(socket_object) das Protokoll ("tcp", "udp", oder "player" fuer normale Playerobjekte), query(_once)_interactive() koennte sowas wie 'tcp:4242' zurueckgeben. Fuer die Remoteadressen hats bereits query_ip_name(), man koennte da noch query_ip_port() hinzufuegen. Ich schreib jetzt mal Funktionsideen auf wie sie mir gerade einfallen (oder in deiner vorigen Mail stehen). Wenn nicht anders gesagt, setzen die Funktionen voraus, dass this_object() das betroffene socketobjekt ist. void net_bind(int portno, string protocol) erzeugt einen Socket fuer Port <portno> (0 fuer 'any') und <protocol> ("udp", "tcp",...) und bindet ihn an this_object(). void net_accept(object obj, mixed extra...) void net_accept(int portno, object obj, mixed extra...) Akzeptiert eine TCP-Verbindung, bindet sie an <obj> und ruft <obj>::_accepted(mixed extra...) auf. In der zweiten Form fuehrt accept() erstmal ein bind(portno, "tcp") wenn this_object kein socketobject ist, bzw. wirft einen Fehler wenn die Portnummer des gebundenen Sockets nicht mit <portno> uebereinstimmt. void net_connect(string remotehost,int remoteport, mixed extra...); void net_connect(int portno, string remotehost,int remoteport, mixed extra...); Versucht eine Verbindung mit der gegebenen Adresse herzustellen. Klappt das, wird this_object::_connected(mixed extra...) aufgerufen. Ist this_object noch kein Socketobjekt, wird vorher noch ein bind(any, "tcp") ausgefuehrt etc. void net_write(string|int* data) Schreibt <data> auf den Socket. void net_read(int amount, mixed extra...) Liest den naechsten Block Daten, max. amount bytes, vom Socket und ruft this_object::_read(int* data, mixed extra...) auf. void net_close(bool noflush) Schliesst den Socket, optional ohne die noch anhaengigen Daten wegzuschreiben. Es braeuchte dann noch einen Fehlercallback, der asynchrone Fehler meldet: void _error(int(string?) type, int errcode, mixed additional...) wobei <type> zwischen connect, accept, read und unexpected close (z.B. durch destruct des Socketobjektes) unterscheidet. Synchrone Fehler in der Ausfuehrung der efuns selber erzeugen ganz normale runtime errors. | ||||
Tags | No tags attached. | ||||
Attached Files | p-020912 (49,928 bytes)
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 <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(); ----------------- 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 <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 #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 ------------------- p-021021 (9,634 bytes)
Short: Socket support From: Ctx@amail.derefence.de Date: 2002-10-21 Type: Patch State: New See also: f-981226-1, f-991104-0, p-020912 Hi Lars ;) Ich habe ein bisschen mit deinem MUD-Driver herumgespielt, er ist wirklich super ;) Ich wollte ihn auch zu Verbindungen mit externen Services wie SMTP, IMAP, POP3, NNTP, IRC usw. usf. einsetzen und hab festgestellt, dass das mit dem ERQ-Daemon eigentlich nur sehr unsauber und hakelig geht. Also dachte ich mir, waere es doch sinnvoll, wenn man den remote service als "Player" sieht, der einem interaktiven objekt Kommandos schickt. Bei einer Verbindung zu einem SMTP-Server z.B. wuerde das Kommando '220' bedeuten, schick mir jetzt deine Befehle. Bei einem IRC-Server mit festem Prefix (:irc-server.de 376) koennte man mit modify_command() die Eingabe in ein entsprechendes Format bringen (Zahlencode zuerst, dann Sender) und hier auch mit add_action arbeiten. Gerade bei nicht-binaeren (Plaintext) Protokollen wie die oben genannten finde ich diese Sichtweise elegant. Ich habe fuer mich den Driver ein wenig geaendert, um eine efun "connect_external" erweitert. connect_external("127.0.0.1", 25, smtp_object); wuerde eine Verbindung zum lokalen SMTP-server herstellen und die Verbindung an das smtp_object binden. Nach gelungenem Verbindungsaufbau wird dort, wie bei player-objekten, logon() aufgerufen. Bei Fehlschlag, bzw. Verbindungsabbruch, wuerde "connerr" mit der entsprechenden errno als Argument aufgerufen. Ansonsten verhaelt sich das objekt wie jedes andere interaktive. Fuer mich ist das ziemlich nuetzlich, vielleicht ja auch fuer dich oder andere. Vermutlich sind noch Bugs darin (Eine Stelle, bei der ich mir ziemlich sicher bin, habe ich mit FIXME markiert), ich hatte leider nicht die Zeit mich in jedes Detail des Drivers einzuarbeiten. Falls der Patch sinnlos/schlecht/falsch ist, schmeiss die Mail einfach weg ;) Ueber kurzes Feedback wuerde ich mich trotzdem freuen. Gruss Florian Heinz (AKA Ctx) diff -ru 3-3/src/comm.c 3-3.ctx/src/comm.c --- 3-3/src/comm.c 2002-10-21 13:06:37.000000000 +0200 +++ 3-3.ctx/src/comm.c 2002-10-21 13:04:56.000000000 +0200 @@ -318,8 +318,15 @@ /* --- Communication sockets --- */ static SOCKET_T sos[MAXNUMPORTS]; + +static SOCKET_T soo[MAXNUMPORTS]; + /* The login sockets. */ +struct soc_item { + int fd; + svalue_t *ob; +} soc[MAXNUMPORTS]; static SOCKET_T udp_s = -1; /* The UDP socket */ @@ -444,7 +451,8 @@ static void send_do(int); static void send_dont(int); static void remove_flush_entry(interactive_t *ip); -static void new_player(SOCKET_T new_socket, struct sockaddr_in *addr, size_t len, int login_port); +static void new_player(SOCKET_T new_socket, struct sockaddr_in *addr, + size_t len, int login_port, svalue_t *n_ob); #ifdef ERQ_DEMON @@ -1198,6 +1206,11 @@ if (socket_number(sos[i]) >= min_nfds) min_nfds = socket_number(sos[i])+1; } /* for(i = 0..numports) */ + + for (i = 0; i < MAXNUMPORTS; i++) { + soc[i].fd = -1; + soc[i].ob = NULL; + } /* We handle SIGPIPEs ourself */ #if defined(__linux__) @@ -1214,6 +1227,46 @@ #endif } /* prepare_ipc() */ +int +connect_ext (char *ip, unsigned short int port, svalue_t *n_ob) +{ + struct sockaddr_in addr; + int fd, st, i; + struct soc_item *sptr = NULL; + + for (i = 0; i < MAXNUMPORTS; i++) { + if (soc[i].fd < 0) { + sptr = &soc[i]; + break; + } + } + + if (!sptr) { + errno = ENOMEM; + return -1; + } + + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip); + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + sptr->fd = fd; + sptr->ob = n_ob; + + set_socket_nonblocking(fd); + set_close_on_exec(fd); + st = connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + if (st < 0) + return -1; + return 0; +} + + /*-------------------------------------------------------------------------*/ void ipc_remove (void) @@ -2168,7 +2221,7 @@ { /* State information: */ - static fd_set readfds; + static fd_set readfds, writefds; /* List of sockets with pending data. * You can ignore a 'could be used uninitialized' warning. */ @@ -2212,13 +2265,20 @@ * of the sockets, but don't wait. */ - /* Set up readfds */ + /* Set up readfds and writefds */ FD_ZERO(&readfds); + FD_ZERO(&writefds); for (i = 0; i < numports; i++) { FD_SET(sos[i], &readfds); } /* for */ nfds = min_nfds; + for (i = 0; i < MAXNUMPORTS; i++) { + if (soc[i].fd >= 0) + FD_SET(soc[i].fd, &writefds); + if (soc[i].fd >= nfds) + nfds = soc[i].fd + 1; + } for (i = max_player + 1; --i >= 0;) { ip = all_players[i]; @@ -2262,7 +2322,7 @@ check_alarm(); timeout.tv_sec = twait; timeout.tv_usec = 0; - res = socket_select(nfds, &readfds, 0, 0, &timeout); + res = socket_select(nfds, &readfds, &writefds, 0, &timeout); if (res == -1) { /* BeOS <= PR2 returns errno -1 instead of EINTR :-( */ @@ -2287,6 +2347,7 @@ * commands. */ FD_ZERO(&readfds); + FD_ZERO(&writefds); } break; } /* for (retries) */ @@ -2573,7 +2634,7 @@ , &length); if ((int)new_socket != -1) new_player(new_socket, &addr, (size_t)length - , port_numbers[i]); + , port_numbers[i], NULL); else if ((int)new_socket == -1 && errno != EWOULDBLOCK && errno != EINTR && errno != EAGAIN && errno != EPROTO ) @@ -2600,6 +2661,27 @@ } } } /* for */ + for (i = 0; i < MAXNUMPORTS; i++) + { + if ((soc[i].fd >= 0) && (FD_ISSET(soc[i].fd, &writefds))) { + int err, errlen = sizeof(err); + getsockopt(soc[i].fd, SOL_SOCKET, SO_ERROR, &err, &errlen); + if (err) { + close(soc[i].fd); + soc[i].fd = -1; + push_number(inter_sp, err); + /* Notify object here */ + apply(STR_CONNERR, soc[i].ob->u.ob, 1); + } else { + struct sockaddr_in addr; + size_t length = sizeof(addr); + + getpeername(soc[i].fd, (struct sockaddr *) &addr, &length); + new_player(soc[i].fd, &addr, length, 0, soc[i].ob); + } + } + } + /* check for alarm signal (heart beat) */ if (comm_time_to_call_heart_beat) { @@ -3227,6 +3309,7 @@ #else , int login_port #endif + , svalue_t *n_ob ) /* Accept (or reject) a new connection on <new_socket> from <addr> (length @@ -3419,7 +3502,10 @@ /* Call master->connect() and evaluate the result. */ - ret = callback_master(STR_CONNECT, 0); + + ret = callback_master(STR_CONNECT, 0); /* FIXME (leak?) */ + if (n_ob) + ret = n_ob; if (new_interactive != O_GET_INTERACTIVE(master_ob)) return; if (ret == NULL @@ -3439,7 +3525,6 @@ O_GET_INTERACTIVE(master_ob) = NULL; master_ob->flags &= ~O_ONCE_INTERACTIVE; check_shadow_sent(master_ob); - assert_shadow_sent(ob); O_GET_INTERACTIVE(ob) = new_interactive; new_interactive->ob = ob; Only in 3-3.ctx/src: comm.c.orig diff -ru 3-3/src/efuns.c 3-3.ctx/src/efuns.c --- 3-3/src/efuns.c 2002-10-11 06:09:44.000000000 +0200 +++ 3-3.ctx/src/efuns.c 2002-10-21 13:06:10.000000000 +0200 @@ -7306,5 +7306,31 @@ return sp; } /* f_utime() */ +/* EFUN connect_external() + * + * void connect_external(string ip, int port, object ob) + * + * Establishs a TCP-Connection to a remote service. + * If the connection succeeds, ob is set interactive, the remote + * service is the commandgiver and logon() is called in the object. + * If the connection fails or aborts, connerr(int errno) is called. + * + */ + +svalue_t * f_connect_external (svalue_t *sp) +{ + if (O_IS_INTERACTIVE(sp->u.ob)) + error("Bad arg 3 to connect_external(): Object is already interactive.\n"); + + connect_ext(get_txt(sp[-2].u.str), sp[-1].u.number, sp); + + free_svalue(sp); + free_svalue(sp - 1); + free_svalue(sp - 2); + return sp - 3; +} + + + /***************************************************************************/ Only in 3-3.ctx/src: efuns.c.orig diff -ru 3-3/src/func_spec 3-3.ctx/src/func_spec --- 3-3/src/func_spec 2002-10-05 21:02:39.000000000 +0200 +++ 3-3.ctx/src/func_spec 2002-10-21 12:58:46.000000000 +0200 @@ -672,4 +672,6 @@ #endif /* USE_DEPRECATED */ +void connect_external(string, int, object); + /***************************************************************************/ Only in 3-3.ctx/src: func_spec.orig diff -ru 3-3/src/string_spec 3-3.ctx/src/string_spec --- 3-3/src/string_spec 2002-10-07 08:04:04.000000000 +0200 +++ 3-3.ctx/src/string_spec 2002-10-21 12:58:46.000000000 +0200 @@ -198,6 +198,8 @@ PC_CHILDREN "children" PC_SHEEP "sheep" +CONNERR "connerr" + #endif /* SUPPLY_PARSE_COMMAND */ /***************************************************************************/ Only in 3-3.ctx/src: string_spec.orig | ||||
External Data (URL) | |||||
|
p-02912: TCP Sockets as implemented by Batmud: 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 |
|
p-021021: Socket support by Ctx: Ich habe ein bisschen mit deinem MUD-Driver herumgespielt, er ist wirklich super ;) Ich wollte ihn auch zu Verbindungen mit externen Services wie SMTP, IMAP, POP3, NNTP, IRC usw. usf. einsetzen und hab festgestellt, dass das mit dem ERQ-Daemon eigentlich nur sehr unsauber und hakelig geht. Also dachte ich mir, waere es doch sinnvoll, wenn man den remote service als "Player" sieht, der einem interaktiven objekt Kommandos schickt. Bei einer Verbindung zu einem SMTP-Server z.B. wuerde das Kommando '220' bedeuten, schick mir jetzt deine Befehle. Bei einem IRC-Server mit festem Prefix (:irc-server.de 376) koennte man mit modify_command() die Eingabe in ein entsprechendes Format bringen (Zahlencode zuerst, dann Sender) und hier auch mit add_action arbeiten. Gerade bei nicht-binaeren (Plaintext) Protokollen wie die oben genannten finde ich diese Sichtweise elegant. Ich habe fuer mich den Driver ein wenig geaendert, um eine efun "connect_external" erweitert. connect_external("127.0.0.1", 25, smtp_object); wuerde eine Verbindung zum lokalen SMTP-server herstellen und die Verbindung an das smtp_object binden. Nach gelungenem Verbindungsaufbau wird dort, wie bei player-objekten, logon() aufgerufen. Bei Fehlschlag, bzw. Verbindungsabbruch, wuerde "connerr" mit der entsprechenden errno als Argument aufgerufen. Ansonsten verhaelt sich das objekt wie jedes andere interaktive. Fuer mich ist das ziemlich nuetzlich, vielleicht ja auch fuer dich oder andere. Vermutlich sind noch Bugs darin (Eine Stelle, bei der ich mir ziemlich sicher bin, habe ich mit FIXME markiert), ich hatte leider nicht die Zeit mich in jedes Detail des Drivers einzuarbeiten. Falls der Patch sinnlos/schlecht/falsch ist, schmeiss die Mail einfach weg ;) Ueber kurzes Feedback wuerde ich mich trotzdem freuen. Gruss Florian Heinz (AKA Ctx) |