/* * socket_efun.c -- socket efuns for MudOS. * 5-92 : Dwayne Fontenot (Jacques@TMI) : original coding. * 10-92 : Dave Richards (Cynosure) : less original coding. */ #include "config.h" #ifdef SOCKET_EFUNS #ifdef SunOS_5 #include <stdlib.h> #include <unistd.h> #endif #include <sys/types.h> #include <sys/time.h> #ifndef LATTICE #include <sys/ioctl.h> #include <sys/socket.h> #endif #ifdef __386BSD__ #include <sys/param.h> #endif #if !defined(apollo) && !defined(linux) && !defined(LATTICE) #include <sys/socketvar.h> #endif #ifdef _AIX #include <sys/select.h> #endif /* _AIX */ #ifndef LATTICE #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #endif #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <ctype.h> #include <signal.h> #ifndef LATTICE #include <memory.h> #else #include "amiga.h" #endif #include "lint.h" #include "buffer.h" #include "interpret.h" #include "object.h" #include "exec.h" #include "debug.h" #include "socket_efuns.h" #include "socket_err.h" extern int errno, d_flag; extern int save_svalue_depth; extern struct svalue const0, const0n; extern char *error_strings[]; struct lpc_socket lpc_socks[MAX_EFUN_SOCKS]; static int socket_name_to_sin PROT((char *, struct sockaddr_in *)); /* * Initialize the LPC efun socket array */ void init_sockets() { int i; debug(8192,("init_sockets: initializing %d socket descriptor(s)\n", MAX_EFUN_SOCKS)); for (i = 0; i < MAX_EFUN_SOCKS; i++) { lpc_socks[i].fd = -1; lpc_socks[i].flags = 0; lpc_socks[i].mode = MUD; lpc_socks[i].state = CLOSED; memset((char *)&lpc_socks[i].l_addr, 0, sizeof (lpc_socks[i].l_addr)); memset((char *)&lpc_socks[i].r_addr, 0, sizeof (lpc_socks[i].r_addr)); lpc_socks[i].name[0] = '\0'; lpc_socks[i].owner_ob = NULL; lpc_socks[i].release_ob = NULL; lpc_socks[i].read_callback[0] = '\0'; lpc_socks[i].write_callback[0] = '\0'; lpc_socks[i].close_callback[0] = '\0'; lpc_socks[i].r_buf = NULL; lpc_socks[i].r_off = 0; lpc_socks[i].r_len = 0; lpc_socks[i].w_buf = NULL; lpc_socks[i].w_off = 0; lpc_socks[i].w_len = 0; } } /* * Create an LPC efun socket */ int socket_create(mode, read_callback, close_callback) enum socket_mode mode; char *read_callback, *close_callback; { int type, i, fd, optval; int binary = 0; if (mode == STREAM_BINARY) { binary = 1; mode = STREAM; } else if (mode == DATAGRAM_BINARY) { binary = 1; mode = DATAGRAM; } switch (mode) { case MUD: case STREAM: type = SOCK_STREAM; break; case DATAGRAM: type = SOCK_DGRAM; break; default: return EEMODENOTSUPP; } for (i = 0; i < MAX_EFUN_SOCKS; i++) { if (lpc_socks[i].state != CLOSED) continue; fd = socket(AF_INET, type, 0); if (fd == -1) { perror("socket_create: socket"); return EESOCKET; } optval = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof (optval)) == -1) { perror("socket_create: setsockopt"); close(fd); return EESETSOCKOPT; } if (set_socket_nonblocking(fd, 1) == -1) { perror("socket_create: set_socket_nonblocking"); close(fd); return EENONBLOCK; } lpc_socks[i].fd = fd; lpc_socks[i].flags = S_HEADER; if (binary) { lpc_socks[i].flags |= S_BINARY; } lpc_socks[i].mode = mode; lpc_socks[i].state = UNBOUND; memset((char *)&lpc_socks[i].l_addr, 0, sizeof (lpc_socks[i].l_addr)); memset((char *)&lpc_socks[i].r_addr, 0, sizeof (lpc_socks[i].r_addr)); lpc_socks[i].name[0] = '\0'; lpc_socks[i].owner_ob = current_object; lpc_socks[i].release_ob = NULL; if (read_callback == NULL) lpc_socks[i].read_callback[0] = '\0'; else strncpy(lpc_socks[i].read_callback, read_callback, CALLBK_BUF_SIZE); lpc_socks[i].write_callback[0] = '\0'; if (type != SOCK_DGRAM && close_callback != NULL) strncpy(lpc_socks[i].close_callback, close_callback, CALLBK_BUF_SIZE); else lpc_socks[i].close_callback[0] = '\0'; lpc_socks[i].r_buf = NULL; lpc_socks[i].r_off = 0; lpc_socks[i].r_len = 0; lpc_socks[i].w_buf = NULL; lpc_socks[i].w_off = 0; lpc_socks[i].w_len = 0; current_object->flags |= O_EFUN_SOCKET; debug(8192,("socket_create: created socket %d mode %d fd %d\n", i, mode, fd)); return i; } return EENOSOCKS; } /* * Bind an address to an LPC efun socket */ int socket_bind(fd, port) int fd, port; { int len; struct sockaddr_in sin; if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE; if (lpc_socks[fd].state == CLOSED) return EEBADF; if (lpc_socks[fd].owner_ob != current_object) return EESECURITY; if (lpc_socks[fd].state != UNBOUND) return EEISBOUND; sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons((u_short)port); if (bind(lpc_socks[fd].fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) { switch (errno) { case EADDRINUSE: return EEADDRINUSE; default: perror("socket_bind: bind"); return EEBIND; } } len = sizeof (sin); if (getsockname(lpc_socks[fd].fd, (struct sockaddr *)&lpc_socks[fd].l_addr, &len) == -1) { perror("socket_bind: getsockname"); return EEGETSOCKNAME; } lpc_socks[fd].state = BOUND; debug(8192,("socket_bind: bound socket %d to %s.%d\n", fd, inet_ntoa(lpc_socks[fd].l_addr.sin_addr), ntohs(lpc_socks[fd].l_addr.sin_port))); return EESUCCESS; } /* * Listen for connections on an LPC efun socket */ int socket_listen(fd, callback) int fd; char *callback; { if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE; if (lpc_socks[fd].state == CLOSED) return EEBADF; if (lpc_socks[fd].owner_ob != current_object) return EESECURITY; if (lpc_socks[fd].mode == DATAGRAM) return EEMODENOTSUPP; if (lpc_socks[fd].state == UNBOUND) return EENOADDR; if (lpc_socks[fd].state != BOUND) return EEISCONN; if (listen(lpc_socks[fd].fd, 5) == -1) { perror("socket_listen: listen"); return EELISTEN; } lpc_socks[fd].state = LISTEN; strncpy(lpc_socks[fd].read_callback, callback, CALLBK_BUF_SIZE); current_object->flags |= O_EFUN_SOCKET; debug(8192,("socket_listen: listen on socket %d\n", fd)); return EESUCCESS; } /* * Accept a connection on an LPC efun socket */ int socket_accept(fd, read_callback, write_callback) int fd; char *read_callback, *write_callback; { int len, accept_fd, i; struct sockaddr_in sin; struct hostent *hp; if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE; if (lpc_socks[fd].state == CLOSED) return EEBADF; if (lpc_socks[fd].owner_ob != current_object) return EESECURITY; if (lpc_socks[fd].mode == DATAGRAM) return EEMODENOTSUPP; if (lpc_socks[fd].state != LISTEN) return EENOTLISTN; lpc_socks[fd].flags &= ~S_WACCEPT; len = sizeof (sin); accept_fd = accept(lpc_socks[fd].fd, (struct sockaddr *)&sin, (int *)&len); if (accept_fd == -1) { perror("socket_accept: accept"); switch (errno) { case EWOULDBLOCK: return EEWOULDBLOCK; case EINTR: return EEINTR; default: perror("socket_accept: accept"); return EEACCEPT; } } for (i = 0; i < MAX_EFUN_SOCKS; i++) { fd_set wmask; struct timeval t; int nb; if (lpc_socks[i].state != CLOSED) continue; lpc_socks[i].fd = accept_fd; lpc_socks[i].flags = S_HEADER | (lpc_socks[fd].flags & S_BINARY); FD_ZERO(&wmask); FD_SET(accept_fd, &wmask); t.tv_sec = 0; t.tv_usec = 0; #ifndef hpux nb = select(FD_SETSIZE, (fd_set *)0, &wmask, (fd_set *)0, &t); #else nb = select(FD_SETSIZE, (int *)0, (int *)&wmask, (int *)0, &t); #endif if (!(FD_ISSET(accept_fd, &wmask))) lpc_socks[i].flags |= S_BLOCKED; lpc_socks[i].mode = lpc_socks[fd].mode; lpc_socks[i].state = DATA_XFER; lpc_socks[i].l_addr = lpc_socks[fd].l_addr; lpc_socks[i].r_addr = sin; lpc_socks[i].owner_ob = NULL; lpc_socks[i].release_ob = NULL; lpc_socks[i].read_callback[0] = '\0'; lpc_socks[i].write_callback[0] = '\0'; lpc_socks[i].close_callback[0] = '\0'; lpc_socks[i].r_buf = NULL; lpc_socks[i].r_off = 0; lpc_socks[i].r_len = 0; lpc_socks[i].w_buf = NULL; lpc_socks[i].w_off = 0; lpc_socks[i].w_len = 0; #ifdef cray /* cray can't take addresses of bitfields */ hp = gethostbyaddr((char *)&sin.sin_addr, (int)sizeof (sin.sin_addr), AF_INET); #else hp = gethostbyaddr((char *)&sin.sin_addr.s_addr, (int)sizeof (sin.sin_addr.s_addr), AF_INET); #endif if (hp != NULL) strncpy(lpc_socks[i].name, hp->h_name, ADDR_BUF_SIZE); else lpc_socks[i].name[0] = '\0'; lpc_socks[i].owner_ob = current_object; strncpy(lpc_socks[i].read_callback, read_callback, CALLBK_BUF_SIZE); strncpy(lpc_socks[i].write_callback, write_callback, CALLBK_BUF_SIZE); strncpy(lpc_socks[i].close_callback, lpc_socks[fd].close_callback, CALLBK_BUF_SIZE); current_object->flags |= O_EFUN_SOCKET; debug(8192,("socket_accept: accept on socket %d\n", fd)); debug(8192,("socket_accept: new socket %d on fd %d\n", i, accept_fd)); return i; } close(accept_fd); return EENOSOCKS; } /* * Connect an LPC efun socket */ int socket_connect(fd, name, read_callback, write_callback) int fd; char *name, *read_callback, *write_callback; { if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE; if (lpc_socks[fd].state == CLOSED) return EEBADF; if (lpc_socks[fd].owner_ob != current_object) return EESECURITY; if (lpc_socks[fd].mode == DATAGRAM) return EEMODENOTSUPP; switch (lpc_socks[fd].state) { case CLOSED: case UNBOUND: case BOUND: break; case LISTEN: return EEISLISTEN; case DATA_XFER: return EEISCONN; } if (!socket_name_to_sin(name, &lpc_socks[fd].r_addr)) return EEBADADDR; strncpy(lpc_socks[fd].read_callback, read_callback, CALLBK_BUF_SIZE); strncpy(lpc_socks[fd].write_callback, write_callback, CALLBK_BUF_SIZE); current_object->flags |= O_EFUN_SOCKET; if (connect(lpc_socks[fd].fd, (struct sockaddr *)&lpc_socks[fd].r_addr, sizeof (struct sockaddr_in)) == -1) { switch (errno) { case EINTR: return EEINTR; case EADDRINUSE: return EEADDRINUSE; case EALREADY: return EEALREADY; case ECONNREFUSED: return EECONNREFUSED; case EINPROGRESS: break; default: perror("socket_connect: connect"); return EECONNECT; } } lpc_socks[fd].state = DATA_XFER; lpc_socks[fd].flags |= S_BLOCKED; return EESUCCESS; } /* * Write a message on an LPC efun socket */ int socket_write(fd, message, name) int fd; struct svalue *message; char *name; { int len, off; char *buf, *p; struct sockaddr_in sin; if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE; if (lpc_socks[fd].state == CLOSED) return EEBADF; if (lpc_socks[fd].owner_ob != current_object) return EESECURITY; if (lpc_socks[fd].mode == DATAGRAM) { if (name == NULL) return EENOADDR; if (!socket_name_to_sin(name, &sin)) return EEBADADDR; } else { if (lpc_socks[fd].state != DATA_XFER) return EENOTCONN; if (name != NULL) return EEBADADDR; if (lpc_socks[fd].flags & S_BLOCKED) return EEALREADY; } switch (lpc_socks[fd].mode) { case MUD: switch (message->type) { case T_OBJECT: return EETYPENOTSUPP; default: save_svalue_depth = 0; len = svalue_save_size(message); if (save_svalue_depth > MAX_SAVE_SVALUE_DEPTH) { return EEBADDATA; } buf = (char *) DMALLOC(sizeof (long) + len + 1, 104, "socket_write: default"); if (buf == NULL) crash_MudOS("Out of memory"); *(long *)buf = htonl((long)len); len += sizeof (long); buf[sizeof(long)] = '\0'; p = buf + sizeof(long); save_svalue(message, &p); break; } break; case STREAM: switch (message->type) { case T_BUFFER: len = message->u.buf->size; buf = (char *)DMALLOC(len, 105, "socket_write: T_BUFFER"); if (buf == NULL) crash_MudOS("Out of memory"); memcpy(buf, message->u.buf->item, len); break; case T_STRING: len = strlen(message->u.string); buf = (char *)DMALLOC(len + 1, 105, "socket_write: T_STRING"); if (buf == NULL) crash_MudOS("Out of memory"); strcpy(buf, message->u.string); break; case T_POINTER: { int i, limit; struct svalue *el; len = message->u.vec->size * sizeof(int); buf = (char *)DMALLOC(len + 1, 105, "socket_write: T_POINTER"); if (buf == NULL) crash_MudOS("Out of memory"); el = message->u.vec->item; limit = len / sizeof(int); for (i=0; i < limit; i++) { switch(el[i].type) { case T_NUMBER: memcpy((char *)&buf[i * sizeof(int)], (char *)&el[i].u.number, sizeof(int)); break; case T_REAL: memcpy((char *)&buf[i * sizeof(int)], (char *)&el[i].u.real, sizeof(int)); break; default: break; } } break; } default: return EETYPENOTSUPP; } break; case DATAGRAM: switch (message->type) { case T_STRING: if (sendto(lpc_socks[fd].fd, message->u.string, strlen(message->u.string) + 1, 0, (struct sockaddr *)&sin, sizeof (sin)) == -1) { perror("socket_write: sendto"); return EESENDTO; } return EESUCCESS; case T_BUFFER: if (sendto(lpc_socks[fd].fd, message->u.buf->item, message->u.buf->size, 0, (struct sockaddr *)&sin, sizeof (sin)) == -1) { perror("socket_write: sendto"); return EESENDTO; } return EESUCCESS; default: return EETYPENOTSUPP; } default: return EEMODENOTSUPP; } off = send(lpc_socks[fd].fd, buf, len, 0); if (off == -1) { FREE(buf); switch (errno) { case EWOULDBLOCK: return EEWOULDBLOCK; default: perror("socket_write: send"); return EESEND; } } if (off < len) { lpc_socks[fd].flags |= S_BLOCKED; lpc_socks[fd].w_buf = buf; lpc_socks[fd].w_off = off; lpc_socks[fd].w_len = len - off; return EECALLBACK; } FREE(buf); return EESUCCESS; } /* * Handle LPC efun socket read select events */ void socket_read_select_handler(fd) int fd; { int cc = 0, addrlen; char buf[BUF_SIZE], addr[ADDR_BUF_SIZE]; struct svalue value; struct sockaddr_in sin; struct object *save_current_object; debug(8192,("read_socket_handler: fd %d state %d\n", fd, lpc_socks[fd].state)); switch (lpc_socks[fd].state) { case CLOSED: case UNBOUND: return; case BOUND: switch (lpc_socks[fd].mode) { case MUD: case STREAM: break; case DATAGRAM: debug(8192,("read_socket_handler: DATA_XFER DATAGRAM\n")); addrlen = sizeof (sin); cc = recvfrom(lpc_socks[fd].fd, buf, sizeof (buf) - 1, 0, (struct sockaddr *)&sin, &addrlen); if (cc <= 0) break; debug(8192,("read_socket_handler: read %d bytes\n", cc)); buf[cc] = '\0'; sprintf(addr, "%s %d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); push_number(fd); if (lpc_socks[fd].flags & S_BINARY) { struct buffer *b; b = allocate_buffer(cc); if (b) { b->ref--; memcpy(b->item, buf, cc); push_buffer(b); } else { push_number(0); } } else { push_string(buf, STRING_MALLOC); } push_string(addr, STRING_MALLOC); debug(8192,("read_socket_handler: apply\n")); save_current_object = current_object; current_object = lpc_socks[fd].owner_ob; safe_apply(lpc_socks[fd].read_callback, lpc_socks[fd].owner_ob, 3); current_object = save_current_object; return; } break; case LISTEN: debug(8192,("read_socket_handler: apply read callback\n")); lpc_socks[fd].flags |= S_WACCEPT; push_number(fd); save_current_object = current_object; current_object = lpc_socks[fd].owner_ob; safe_apply(lpc_socks[fd].read_callback, lpc_socks[fd].owner_ob, 1); current_object = save_current_object; return; case DATA_XFER: switch (lpc_socks[fd].mode) { case DATAGRAM: break; case MUD: debug(8192,("read_socket_handler: DATA_XFER MUD\n")); if (lpc_socks[fd].flags & S_HEADER) { cc = recv(lpc_socks[fd].fd, (char *)&lpc_socks[fd].r_len + lpc_socks[fd].r_off, sizeof (long) - lpc_socks[fd].r_off, 0); if (cc <= 0) break; debug(8192,("read_socket_handler: read %d bytes\n", cc)); lpc_socks[fd].r_off += cc; if (lpc_socks[fd].r_off != sizeof (long)) return; debug(8192,("read_socket_handler: read header\n")); lpc_socks[fd].flags &= ~S_HEADER; lpc_socks[fd].r_off = 0; lpc_socks[fd].r_len = ntohl(lpc_socks[fd].r_len); lpc_socks[fd].r_buf = (char *) DMALLOC(lpc_socks[fd].r_len + 1, 106, "socket_read_select_handler"); if (lpc_socks[fd].r_buf == NULL) crash_MudOS("Out of memory"); debug(8192,("read_socket_handler: svalue len is %d\n", lpc_socks[fd].r_len)); } if (lpc_socks[fd].r_off < lpc_socks[fd].r_len) { cc = recv(lpc_socks[fd].fd, lpc_socks[fd].r_buf + lpc_socks[fd].r_off, lpc_socks[fd].r_len - lpc_socks[fd].r_off, 0); if (cc <= 0) break; debug(8192,("read_socket_handler: read %d bytes\n", cc)); lpc_socks[fd].r_off += cc; if (lpc_socks[fd].r_off != lpc_socks[fd].r_len) return; debug(8192,("read_socket_handler: read svalue\n")); } lpc_socks[fd].r_buf[lpc_socks[fd].r_len] = '\0'; value = const0; push_number(fd); if (restore_svalue(lpc_socks[fd].r_buf, &value) == 0) push_svalue(&value); else push_null(); FREE(lpc_socks[fd].r_buf); lpc_socks[fd].flags |= S_HEADER; lpc_socks[fd].r_buf = NULL; lpc_socks[fd].r_off = 0; lpc_socks[fd].r_len = 0; debug(8192,("read_socket_handler: apply read callback\n")); save_current_object = current_object; current_object = lpc_socks[fd].owner_ob; safe_apply(lpc_socks[fd].read_callback, lpc_socks[fd].owner_ob, 2); current_object = save_current_object; break; case STREAM: debug(8192,("read_socket_handler: DATA_XFER STREAM\n")); cc = recv(lpc_socks[fd].fd, buf, sizeof (buf) - 1, 0); if (cc <= 0) break; debug(8192,("read_socket_handler: read %d bytes\n", cc)); buf[cc] = '\0'; push_number(fd); if (lpc_socks[fd].flags & S_BINARY) { struct buffer *b; b = allocate_buffer(cc); if (b) { b->ref--; memcpy(b->item, buf, cc); push_buffer(b); } else { push_number(0); } } else { push_string(buf, STRING_MALLOC); } debug(8192,("read_socket_handler: apply read callback\n")); save_current_object = current_object; current_object = lpc_socks[fd].owner_ob; safe_apply(lpc_socks[fd].read_callback, lpc_socks[fd].owner_ob, 2); current_object = save_current_object; return; } break; } if(cc == -1){ switch(errno){ case EINTR: case EWOULDBLOCK: return; default: break; } } save_current_object = current_object; current_object = lpc_socks[fd].owner_ob; socket_close(fd); current_object = save_current_object; debug(8192,("read_socket_handler: apply close callback\n")); push_number(fd); save_current_object = current_object; current_object = lpc_socks[fd].owner_ob; safe_apply(lpc_socks[fd].close_callback, lpc_socks[fd].owner_ob, 1); current_object = save_current_object; } /* * Handle LPC efun socket write select events */ void socket_write_select_handler(fd) int fd; { int cc; struct object *save_current_object; debug(8192,("write_socket_handler: fd %d state %d\n", fd, lpc_socks[fd].state)); if ((lpc_socks[fd].flags & S_BLOCKED) == 0) return; if (lpc_socks[fd].w_buf != NULL) { cc = send(lpc_socks[fd].fd, lpc_socks[fd].w_buf + lpc_socks[fd].w_off, lpc_socks[fd].w_len, 0); if (cc == -1) return; lpc_socks[fd].w_off += cc; lpc_socks[fd].w_len -= cc; if (lpc_socks[fd].w_len != 0) return; FREE(lpc_socks[fd].w_buf); lpc_socks[fd].w_buf = NULL; lpc_socks[fd].w_off = 0; } lpc_socks[fd].flags &= ~S_BLOCKED; debug(8192,("write_socket_handler: apply write_callback\n")); push_number(fd); save_current_object = current_object; current_object = lpc_socks[fd].owner_ob; safe_apply(lpc_socks[fd].write_callback, lpc_socks[fd].owner_ob, 1); current_object = save_current_object; } /* * Close an LPC efun socket */ int socket_close(fd) int fd; { if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE; if (lpc_socks[fd].state == CLOSED) return EEBADF; if (lpc_socks[fd].owner_ob != current_object) return EESECURITY; while (close(lpc_socks[fd].fd) == -1 && errno == EINTR) ; /* empty while */ lpc_socks[fd].state = CLOSED; if (lpc_socks[fd].r_buf != NULL) FREE(lpc_socks[fd].r_buf); if (lpc_socks[fd].w_buf != NULL) FREE(lpc_socks[fd].w_buf); debug(8192,("socket_close: closed fd %d\n", fd)); return EESUCCESS; } /* * Release an LPC efun socket to another object */ int socket_release(fd, ob, callback) int fd; struct object *ob; char *callback; { struct object *save_current_object; if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE; if (lpc_socks[fd].state == CLOSED) return EEBADF; if (lpc_socks[fd].owner_ob != current_object) return EESECURITY; if (lpc_socks[fd].flags & S_RELEASE) return EESOCKRLSD; lpc_socks[fd].flags |= S_RELEASE; lpc_socks[fd].release_ob = ob; push_number(fd); push_object(ob); save_current_object = current_object; current_object = ob; safe_apply(callback, ob, 2); current_object = save_current_object; if ((lpc_socks[fd].flags & S_RELEASE) == 0) return EESUCCESS; lpc_socks[fd].flags &= ~S_RELEASE; lpc_socks[fd].release_ob = NULL; return EESOCKNOTRLSD; } /* * Aquire an LPC efun socket from another object */ int socket_acquire(fd, read_callback, write_callback, close_callback) int fd; char *read_callback, *write_callback, *close_callback; { if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE; if (lpc_socks[fd].state == CLOSED) return EEBADF; if ((lpc_socks[fd].flags & S_RELEASE) == 0) return EESOCKNOTRLSD; if (lpc_socks[fd].release_ob != current_object) return EESECURITY; lpc_socks[fd].flags &= ~S_RELEASE; lpc_socks[fd].owner_ob = current_object; lpc_socks[fd].release_ob = NULL; strncpy(lpc_socks[fd].read_callback, read_callback, CALLBK_BUF_SIZE); strncpy(lpc_socks[fd].write_callback, write_callback, CALLBK_BUF_SIZE); strncpy(lpc_socks[fd].close_callback, close_callback, CALLBK_BUF_SIZE); return EESUCCESS; } /* * Return the string representation of a socket error */ char * socket_error(error) int error; { error = -(error + 1); if (error < 0 || error >= ERROR_STRINGS) return "socket_error: invalid error number"; return error_strings[error]; } /* * Return the remote address for an LPC efun socket */ int get_socket_address(fd, addr, port) int fd, *port; char *addr; { if (fd < 0 || fd >= MAX_EFUN_SOCKS) { addr[0] = '\0'; *port = 0; return EEFDRANGE; } *port = (int)ntohs(lpc_socks[fd].r_addr.sin_port); sprintf(addr, "%s", inet_ntoa(lpc_socks[fd].r_addr.sin_addr)); return EESUCCESS; } /* * Return the current socket owner */ struct object * get_socket_owner(fd) int fd; { if (fd < 0 || fd >= MAX_EFUN_SOCKS) return (struct object *)NULL; if (lpc_socks[fd].state == CLOSED) return (struct object *)NULL; return lpc_socks[fd].owner_ob; } /* * Initialize a T_OBJECT svalue */ void assign_socket_owner(sv, ob) struct svalue *sv; struct object *ob; { if (ob != NULL) { sv->type = T_OBJECT; sv->u.ob = ob; add_ref(ob, "assign_socket_owner"); } else assign_svalue_no_free(sv, &const0n); } /* * Convert a string representation of an address to a sockaddr_in */ static int socket_name_to_sin(name, sin) char *name; struct sockaddr_in *sin; { 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); sin->sin_family = AF_INET; sin->sin_port = htons((u_short)port); sin->sin_addr.s_addr = inet_addr(addr); return 1; } /* * Close any sockets owned by ob */ void close_referencing_sockets(ob) struct object *ob; { int i; struct object *save_current_object; save_current_object = current_object; current_object = ob; for (i = 0; i < MAX_EFUN_SOCKS; i++) if (lpc_socks[i].owner_ob == ob && lpc_socks[i].state != CLOSED) socket_close(i); current_object = save_current_object; } /* * Return the string representation of a sockaddr_in */ static char * inet_address(sin) struct sockaddr_in *sin; { static char addr[23], port[7]; if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY) strcpy(addr, "*"); else strcpy(addr, inet_ntoa(sin->sin_addr)); strcat(addr, "."); if (ntohs(sin->sin_port) == 0) strcpy(port, "*"); else sprintf(port, "%d", ntohs(sin->sin_port)); strcat(addr, port); return(addr); } /* * Dump the LPC efun socket array */ void dump_socket_status() { int i; add_message("Fd State Mode Local Address Remote Address\n"); add_message("-- --------- -------- --------------------- ---------------------\n"); for(i = 0; i < MAX_EFUN_SOCKS; i++) { add_message("%2d ", lpc_socks[i].fd); switch(lpc_socks[i].state){ case CLOSED: add_message(" CLOSED "); break; case UNBOUND: add_message(" UNBOUND "); break; case BOUND: add_message(" BOUND "); break; case LISTEN: add_message(" LISTEN "); break; case DATA_XFER: add_message("DATA_XFER"); break; default: add_message(" ?? "); break; } add_message(" "); switch(lpc_socks[i].mode){ case MUD: add_message(" MUD "); break; case STREAM: add_message(" STREAM "); break; case DATAGRAM: add_message("DATAGRAM"); break; default: add_message(" ?? "); break; } add_message(" "); add_message("%-21s ", inet_address(&lpc_socks[i].l_addr)); add_message("%-21s\n", inet_address(&lpc_socks[i].r_addr)); } } #endif /* SOCKET_EFUNS */