#include "prims.h" #include "config.h" #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <ctype.h> #include <netdb.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> typedef struct muf_data { int descriptor, connected, port; long connected_at, last_time; dbref uid; char *hostname; FILE *fd; struct muf_data *next, **prev; } muf_data; static int nmufs = 0; /* For tracking MUF TCP sockets */ muf_data *muf_list = 0; /* Init MUF TCP socket linked list */ #define MUF_READ 1 #define MUF_WRITE 0 /* private globals */ extern inst *p_oper1, *p_oper2, *p_oper3, *p_oper4; extern int p_result; extern int p_nargs; extern dbref p_ref; extern char p_buf[BUFFER_LEN]; static descriptor_data *dd; extern int errno; int desc_count() { descriptor_data *d; int returnval = 0; for (d = descriptor_list; d; d = d->next, returnval++); return returnval; } descriptor_data *desc_num(int n) { descriptor_data *return_desc; for (return_desc = descriptor_list; return_desc; return_desc = return_desc->next) if (return_desc->descriptor == n) break; return return_desc; } /**************************************** * concount ( -- i ) - get number of connections to a MUCK ****************************************/ void prims_concount (__P_PROTO) { if (*top >= STACK_SIZE) abort_interp("Stack Overflow!"); p_result = desc_count(); push(arg, top, PROG_INTEGER, MIPSCAST &p_result); } /**************************************** * connections ( -- i1...iN N ) - get list of descriptors ****************************************/ void prims_connections (__P_PROTO) { for (dd = descriptor_list, p_result = 0; dd; dd = dd->next) { if (*top >= STACK_SIZE) abort_interp("Stack Overflow!"); if (!dd->connected || (!(FLAGS(dd->player) & DARK) || fr->wizard)) { push(arg, top, PROG_INTEGER, MIPSCAST &(dd->descriptor)); p_result++; } } if (*top >= STACK_SIZE) abort_interp("Stack Overflow!"); push(arg, top, PROG_INTEGER, MIPSCAST &p_result); } /**************************************** * condbref ( i -- d ) - get dbref of a certain descriptor ****************************************/ void prims_condbref (__P_PROTO) { CHECKOP(1); p_oper1 = POP(); if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer."); dd = desc_num(p_oper1->data.number); if (!dd) abort_interp("Illegal descriptor #."); p_ref = (dd->connected) ? dd->player : NOTHING; CLEAR(p_oper1); push(arg, top, PROG_OBJECT, MIPSCAST &p_ref); } /**************************************** * conidle ( i -- i ) - get idle time for a connection ****************************************/ void prims_conidle (__P_PROTO) { long now; CHECKOP(1); p_oper1 = POP(); if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer."); p_result = p_oper1->data.number; dd = desc_num(p_result); if (!dd) abort_interp("Incorrect descriptor #."); (void) time (&now); p_result = now - dd->last_time; CLEAR(p_oper1); push(arg, top, PROG_INTEGER, MIPSCAST &p_result); } /**************************************** * contime ( i -- i ) - get connection time for a descriptor ****************************************/ void prims_contime (__P_PROTO) { long now; CHECKOP(1); p_oper1 = POP(); if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer."); dd = desc_num(p_oper1->data.number); if (!dd) abort_interp("Incorrect descriptor #."); (void) time (&now); p_result = now - dd->connected_at; CLEAR(p_oper1); push(arg, top, PROG_INTEGER, MIPSCAST &p_result); } /**************************************** * conhost ( i -- s ) - get connection site for a descriptor ****************************************/ void prims_conhost (__P_PROTO) { CHECKOP(1); p_oper1 = POP(); if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer."); dd = desc_num(p_oper1->data.number); if (!fr->wizard && (!dd->connected || !controls(fr->euid, dd->player))) abort_interp("Permission denied."); if (!dd) abort_interp("Invalid connection #."); CLEAR(p_oper1); push(arg, top, PROG_STRING, MIPSCAST dup_string(dd->hostname)); } /**************************************** * conboot ( i -- ) - boot off a connection ****************************************/ void prims_conboot (__P_PROTO) { CHECKOP(1); p_oper1 = POP(); if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer."); dd = desc_num(p_oper1->data.number); if (!fr->wizard && (!dd->connected || !controls(fr->euid, dd->player))) abort_interp("Permission denied."); if (!dd) abort_interp("Invalid connection #."); process_output(dd); shutdownsock(dd); CLEAR(p_oper1); } /**************************************** * connotify ( i s -- ) - notify a connection ****************************************/ void prims_connotify (__P_PROTO) { CHECKOP(2); p_oper2 = POP(); p_oper1 = POP(); if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer."); if (p_oper2->type != PROG_STRING) abort_interp("Argument not an string."); dd = desc_num(p_oper1->data.number); if (!fr->wizard && (!dd->connected || !controls(fr->euid, dd->player))) abort_interp("Permission denied."); if (!dd) abort_interp("Invalid connection #."); queue_string (dd, p_oper2->data.string); queue_write (dd, "\r\n", 2); CLEAR(p_oper1); CLEAR(p_oper2); } /**************************************** * connnected? ( i -- i ) - returns 1 if descriptor is connected ****************************************/ void prims_connected (__P_PROTO) { int res = 1; CHECKOP(1); p_oper1 = POP(); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument."); dd = desc_num(p_oper1->data.number); if (!dd) res = 0; CLEAR(p_oper1); push(arg, top, PROG_INTEGER, MIPSCAST &res); } void prims_awakep (__P_PROTO) { CHECKOP(1); p_oper1 = POP(); for (dd = descriptor_list, p_result = 0; dd; dd = dd->next) { if (*top >= STACK_SIZE) abort_interp("Stack Overflow!"); if (dd->connected && (dd->player == p_oper1->data.objref) && (!(FLAGS(dd->player) & DARK) || fr->wizard)) p_result++; } CLEAR(p_oper1); push(arg, top, PROG_INTEGER, MIPSCAST &p_result); } void prims_conlast (__P_PROTO) { descriptor_data *tempd; p_result = -1; CHECKOP(1); p_oper1 = POP(); if (!valid_object(p_oper1)) abort_interp("Invalid object."); if (Typeof(p_oper1->data.objref) != TYPE_PLAYER) abort_interp("Non-Player argument."); for(tempd = descriptor_list; tempd; tempd = tempd->next) if(tempd->connected && tempd->player == p_oper1->data.objref) { p_result = tempd->descriptor; break; } CLEAR(p_oper1); push(arg, top, PROG_INTEGER, MIPSCAST &p_result); } void prims_pconnectors (__P_PROTO) { descriptor_data *tempd; int i =0; p_result = -1; CHECKOP(1); p_oper1 = POP(); if (!valid_object(p_oper1)) abort_interp("Invalid object."); if (Typeof(p_oper1->data.objref) != TYPE_PLAYER) abort_interp("Non-Player argument."); for(tempd = descriptor_list; tempd; tempd = tempd->next) { if(tempd->connected && tempd->player == p_oper1->data.objref) { p_result = tempd->descriptor; push(arg, top, PROG_INTEGER, MIPSCAST &p_result); i++; } } CLEAR(p_oper1); push(arg, top, PROG_INTEGER, MIPSCAST &i); } muf_data *check_muf_desc(int i) { muf_data *d; for (d = muf_list; d; d = d->next) { if(d->descriptor == i) return d; } return NULL; } int close_muf(muf_data *d) { int i; log_status("CLOSE MUF: descriptor %d\n", d->descriptor); i = close(d->descriptor) + 1; if(d->hostname) free(d->hostname); fclose(d->fd); *d->prev = d->next; if (d->next) d->next->prev = d->prev; if(d) free(d); nmufs--; return i; } int wread(int fd, int iobit) { fd_set writebits, readbits; struct timeval timer; char garbage[1]; int ret; timer.tv_sec = 0; timer.tv_usec = 100; FD_ZERO(&writebits); FD_ZERO(&readbits); if(iobit == MUF_WRITE) FD_SET(fd, &writebits); else if(iobit == MUF_READ) FD_SET(fd, &readbits); if (select(fd+1, &readbits, &writebits, (fd_set *)NULL, &timer) < 0) if (errno != EINTR) panic("Select failed in wread."); ret = recv(fd, garbage, 0, MSG_PEEK); /* select() lies on */ if (ret == -1 && errno != EINTR) /* some machines */ return 0; if(iobit == MUF_WRITE) { if (FD_ISSET(fd, &writebits)) return 1; } else if (iobit == MUF_READ) { if (FD_ISSET(fd, &readbits)) return 1; } return 0; } int get_host_address(char *name, struct in_addr * addr) { /* Taken from Tinytalk version 117 */ struct hostent *blob; union { /* %#@!%!@%#!@ idiot who designed */ long signed_thingy; /* the inetaddr routine.... */ unsigned long unsigned_thingy; } thingy; if (!*name) return (0); if ((*name >= '0') && (*name <= '9')) { /* IP address. */ addr->s_addr = inet_addr(name); thingy.unsigned_thingy = addr->s_addr; if (thingy.signed_thingy == -1) { return (0); } } else { /* Host name. */ blob = gethostbyname(name); if (blob == NULL) { return (0); } bcopy(blob->h_addr, (char *) addr, sizeof(struct in_addr)); } return (1); /* Success. */ } void prims_socket (__P_PROTO) { muf_data *d; int s; if(!fr->wizard) abort_interp("Permission denied."); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { abort_interp("Socket could not be created."); } else { nmufs++; if(!(d = (muf_data *) malloc(sizeof(muf_data)))) panic("Out of memory in prims_socket."); make_nonblocking(s); d->descriptor = s; /* Fill out the structure with info */ d->connected_at = 0; d->port = 0; d->last_time = 0; d->uid = fr->euid; d->hostname = NULL; d->fd = fdopen(s, "r"); if (muf_list) muf_list->prev = &d->next; d->next = muf_list; d->prev = &muf_list; muf_list = d; p_result = s; } push(arg, top, PROG_INTEGER, MIPSCAST &p_result); } void prims_connect (__P_PROTO) { struct in_addr host_address; struct sockaddr_in socket_address; muf_data *d; int err; CHECKOP(3); p_oper1 = POP(); p_oper2 = POP(); p_oper3 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper2->type != PROG_STRING) abort_interp("Non-string argument (2)"); if (!p_oper2->data.string) abort_interp("NULL string argument (2)"); if (p_oper1->type != PROG_INTEGER || p_oper3->type != PROG_INTEGER) abort_interp("Non integer argument"); if(!(d = check_muf_desc(p_oper3->data.number))) abort_interp("Invalid socket."); if(p_oper1->data.number <= 0 ) abort_interp("Invalid port."); if (!get_host_address(p_oper2->data.string, &host_address)) abort_interp("Invalid host address."); socket_address.sin_family = AF_INET; socket_address.sin_port = htons(p_oper1->data.number); bcopy((char *) &host_address, (char *) &socket_address.sin_addr, sizeof(struct in_addr)); err = connect(p_oper3->data.number, &socket_address, sizeof(struct sockaddr_in)); if (err < 0 && errno != EINPROGRESS) { close_muf(d); p_result = 0; } else { d->connected_at = time(NULL); d->hostname = dup_string(p_oper2->data.string); d->port = p_oper1->data.number; p_result = 1; } push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); CLEAR(p_oper2); CLEAR(p_oper3); } void prims_close (__P_PROTO) { muf_data *d; CHECKOP(1); p_oper1 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if((d = check_muf_desc(p_oper1->data.number))) { p_result = close_muf(d); } else p_result = -1; push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); } void prims_ready_write (__P_PROTO) { muf_data *d; CHECKOP(1); p_oper1 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if(!(d = check_muf_desc(p_oper1->data.number))) p_result = -1; else { p_result = wread(p_oper1->data.number, MUF_WRITE); } push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); } void prims_ready_read (__P_PROTO) { muf_data *d; CHECKOP(1); p_oper1 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if(!(d = check_muf_desc(p_oper1->data.number))) p_result = -1; else { p_result = wread(p_oper1->data.number, MUF_READ); } push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); } void prims_socket_read (__P_PROTO) { muf_data *d; CHECKOP(2); p_oper1 = POP(); p_oper2 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument (2)"); if (p_oper2->type != PROG_INTEGER) abort_interp("Non-integer argument (1)"); if (p_oper1->data.number <= 0) abort_interp("Invalid number of bytes (2)"); if(!(d = check_muf_desc(p_oper2->data.number))) { abort_interp("Invalid socket"); } else { if(!wread(p_oper2->data.number, MUF_READ)) abort_interp("Socket not ready for read"); if(p_oper1->data.number > BUFFER_LEN) abort_interp("Buffer is too big!"); for( p_result=0; p_result < BUFFER_LEN; p_result++) p_buf[p_result] = '\0'; p_result=read(p_oper2->data.number, p_buf, p_oper1->data.number); if(p_result == -1) close_muf(d); else d->last_time = time(NULL); } push(arg, top, PROG_STRING, MIPSCAST &p_buf); push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); CLEAR(p_oper2); } void prims_socket_write (__P_PROTO) { muf_data *d; char tmp[BUFFER_LEN+1]; char tmp2[BUFFER_LEN+1]; char *ptr1, *ptr2; CHECKOP(2); p_oper1 = POP(); p_oper2 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_STRING) abort_interp("Non-string argument (1)"); if (!p_oper1->data.string )abort_interp("NULL string argument (2)"); if (p_oper2->type != PROG_INTEGER) abort_interp("Non-integer argument (2)"); if(!(d = check_muf_desc(p_oper2->data.number))) abort_interp("Invalid socket."); if(!wread(p_oper2->data.number, MUF_WRITE)) abort_interp("Socket not ready for write"); strncpy(tmp, p_oper1->data.string, strlen(p_oper1->data.string)+1); ptr2 = tmp; ptr1 = tmp2; while(*ptr2) { if(*ptr2 == '%') { ptr2++; if (*ptr2 == 'r') *ptr1++ = '\r'; else if(*ptr2 == 'n') *ptr1++ = '\n'; else *ptr1++ = *ptr2; } else *ptr1++ = *ptr2; ptr2++; } *ptr1++ = '\0'; /* p_result=write(p_oper2->data.number, tmp2, strlen(tmp2)); */ p_result=write_data(p_oper2->data.number, tmp2, strlen(tmp2)); if(p_result == -1 && (errno != EWOULDBLOCK)) close_muf(d); else d->last_time = time(NULL); push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); CLEAR(p_oper2); } void prims_socket_last (__P_PROTO) { muf_data *d; CHECKOP(1); p_oper1 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if(!(d = check_muf_desc(p_oper1->data.number))) p_result = -1; else { p_result = d->last_time; } push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); } void prims_socket_connected_at (__P_PROTO) { muf_data *d; CHECKOP(1); p_oper1 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if(!(d = check_muf_desc(p_oper1->data.number))) p_result = -1; else { p_result = d->connected_at; } push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); } void prims_socket_connected (__P_PROTO) { struct in_addr host_address; struct sockaddr_in socket_address; int err; muf_data *d; CHECKOP(1); p_oper1 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if(!(d = check_muf_desc(p_oper1->data.number))) abort_interp("Invalid socket"); if (!get_host_address(d->hostname, &host_address)) abort_interp("Invalid host address. THIS SHOULD NOT HAPPEN!"); socket_address.sin_family = AF_INET; socket_address.sin_port = htons(d->port); bcopy((char *) &host_address, (char *) &socket_address.sin_addr, sizeof(struct in_addr)); err = connect(d->descriptor, &socket_address, sizeof(struct sockaddr_in)); if(err < 0) { switch(errno) { case EALREADY: case EINPROGRESS: case EINTR: p_result = 0; break; case EISCONN: p_result = 1; break; default: close_muf(d); p_result = -1; break; } } else p_result = 1; push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); } void prims_socket_host (__P_PROTO) { muf_data *d; CHECKOP(1); p_oper1 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if(!(d = check_muf_desc(p_oper1->data.number))) strcpy(p_buf, ""); else { if(d->hostname) strcpy(p_buf, d->hostname); else strcpy(p_buf, ""); } push(arg, top, PROG_STRING, MIPSCAST &p_buf); CLEAR(p_oper1); } void prims_socket_fgets (__P_PROTO) { muf_data *d; CHECKOP(1); p_oper1 = POP(); if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if(!(d = check_muf_desc(p_oper1->data.number))) abort_interp("Invalid descriptor."); if(!d->fd) abort_interp("Couldn't open stream."); if(!fgets(p_buf, sizeof(p_buf), d->fd)) strcpy(p_buf, ""); push(arg, top, PROG_STRING, MIPSCAST &p_buf); } void prims_is_socket (__P_PROTO) { muf_data *d; CHECKOP(1); p_oper1 = POP(); p_result = 0; if(!fr->wizard) abort_interp("Permission denied."); if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument"); if((d = check_muf_desc(p_oper1->data.number))) p_result = 1; push(arg, top, PROG_INTEGER, MIPSCAST &p_result); CLEAR(p_oper1); } void prims_xevent_pop (__P_PROTO) { CHECKOP(1); p_oper1 = POP(); #ifdef XEVENTS if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer."); dd = desc_num(p_oper1->data.number); if (!dd) abort_interp("Illegal descriptor #."); if (!dd->connected) abort_interp("Descriptor not connected."); if (!fr->wizard || !controls(fr->euid, dd->player)) abort_interp("Permission denied."); if(dd->q && dd->q->comm) { strcpy(p_buf, dd->q->comm); free_queue(dd); } else strcpy(p_buf, ""); #else abort_interp("XEVENTS not enabled on this server."); #endif CLEAR(p_oper1); push(arg, top, PROG_STRING, MIPSCAST dup_string(p_buf)); } void prims_xevent_flush (__P_PROTO) { CHECKOP(1); p_oper1 = POP(); #ifdef XEVENTS if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer."); dd = desc_num(p_oper1->data.number); if (!dd) abort_interp("Illegal descriptor #."); if (!dd->connected) abort_interp("Descriptor not connected."); if (!fr->wizard || !controls(fr->euid, dd->player)) abort_interp("Permission denied."); if(dd->q) { while(dd->q) free_queue(dd); } #else abort_interp("XEVENTS not enabled on this server."); #endif } int write_data(s, buffer, len) int s; char *buffer; int len; { int numwritten; while (len > 0) { if ((numwritten = write(s, buffer, len)) == -1) { if(errno == EWOULDBLOCK) { numwritten = 0; /* wread(d->descriptor); */ } else return -1; /* ACK! Better be in a child! */ } len -= numwritten; buffer += numwritten; } return numwritten; }