musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/* 
 * MusicMUD Daemon, version 1.0
 * Copyright (C) 1998-2003 Abigail Brady
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>

#include "Socket.h"
#include "log.h"
#include "privs.h"
#include "musicio.h"
#include "pflags.h"
#include "config.h"

bool sock_debug = false;

InAddr::~InAddr() {
}

class InAddr4 : public InAddr {
  struct sockaddr_in s;
public:
  InAddr4(const struct in_addr *si) {
    s.sin_family = AF_INET;
    s.sin_port = 0;
    s.sin_addr = *si;
  }
  InAddr4(const struct sockaddr_in *si) : s(*si) { }
  virtual ~InAddr4() { }
  void set_port(int port) { s.sin_port = htons(port); }
  int get_port() const { return ntohs(s.sin_port); }
  virtual string tostring() const {
    return inet_ntoa(s.sin_addr);
  }
  sockaddr *addr() const {
    return (sockaddr*)&s;
  }
  socklen_t addr_len() const {
    return sizeof s;
  }
  int af() const { return AF_INET; };
  operator sockaddr_storage() const {
    sockaddr_storage st;
    memcpy(&st, &s, sizeof s);
    return st;
  }
  hostent *gethost() {
    return gethostbyaddr((const char *)&s.sin_addr, sizeof s.sin_addr, AF_INET);
  }
};

#ifdef AF_INET6
class InAddr6 : public InAddr {
  struct sockaddr_in6 s;
public:
  InAddr6(const struct in6_addr *si) {
    s.sin6_family = AF_INET6;
    s.sin6_port = 0;
    s.sin6_addr = *si;
  }
  InAddr6(const struct sockaddr_in6 *si) : s(*si) {
  }
  virtual ~InAddr6() { };
  void set_port(int port) { s.sin6_port = htons(port); };
  int get_port() const { return s.sin6_port; }
  virtual string tostring() const {
    char dest[100];
    inet_ntop(AF_INET6, &s.sin6_addr, dest, 100);
    dest[99] = 0;
    if (IN6_IS_ADDR_V4MAPPED(&s.sin6_addr)) {
      return dest+7;
    }
    return dest;
  }
  sockaddr *addr() const {
    return (sockaddr*)&s;
  }
  socklen_t addr_len() const {
    return sizeof s;
  }
  int af() const { return AF_INET6; };
  bool is6() const {
   return !IN6_IS_ADDR_V4MAPPED(&s.sin6_addr);
  }
  operator sockaddr_storage() const {
    sockaddr_storage st;
    memcpy(&st, &s, sizeof s);
    return st;
  }
  hostent *gethost() {
    return gethostbyaddr((const char *)&s.sin6_addr, sizeof s.sin6_addr, AF_INET6);
  }
};
#endif

InAddr *InAddr::listener(int af, int port)
{
#ifdef AF_INET6
  if (af==AF_INET6) {
    struct sockaddr_in6 a;
    a.sin6_family = AF_INET6;
    a.sin6_port = htons(port);
    a.sin6_addr = in6addr_any;
    return create((sockaddr*)&a);
  }
#endif
  if (af==AF_INET) {
    struct sockaddr_in a;
    a.sin_family = AF_INET6;
    a.sin_port = htons(port);
    a.sin_addr.s_addr = INADDR_ANY;
    return create((sockaddr*)&a);
  }

  return 0;
}

InAddr *InAddr::create(const sockaddr *addr)
{
  if (addr->sa_family == AF_INET) {
    return new InAddr4((const struct sockaddr_in *)addr);
  }
#ifdef AF_INET6
  if (addr->sa_family == AF_INET6) {
    return new InAddr6((const struct sockaddr_in6 *)addr);
  }
#endif
  return NULL;
}

InAddr *InAddr::getpeername(int fd)
{
  sockaddr_storage s;
  socklen_t l = sizeof s;
  ::getpeername(fd, (sockaddr*)&s, &l);
  return create((sockaddr*)&s);
}

InAddr *InAddr::getsockname(int fd)
{
  sockaddr_storage s;
  socklen_t l = sizeof s;
  ::getsockname(fd, (sockaddr*)&s, &l);
  return create((sockaddr*)&s);
}

string esc(const char *q, int size) {
  string t = "";
  const char *l = q + size;
  while (q < l) {
    if (*q < 0x20) {
      if (*q==033) t+= "\\e";
      else if (*q=='\r') t+= "\\r";
      else if (*q=='\n') t+= "\\n";
      else if (*q=='\a') t+= "\\a";
      else {
	char blah[5];
	sprintf(blah, "\\x%02x", (unsigned char )*q);
	t += blah;
      }
      q++;
      continue;
    }
    if (*q == '\\' || *q=='\"') t += '\\';
    t += *q;
    q++;
  }
  return t;
}

Socket::Socket() : fd(-1), that_addr(0), this_addr(0), port(-1) {
}

Socket::Socket(int port) {
  this->port = port;
  buffer="";
  listener = true;
  int af;

  fd = socket(af = AF_INET6, SOCK_STREAM, 0);
  if (fd == -1) {
    fd = socket(af = AF_INET, SOCK_STREAM, 0);

    if (fd==-1) {
      log(PFL_SEEINFO, 0, "socket", "port %i %s : not accepting connections", port, strerror(errno));
      dead = true;
      return;
    }
  }
  char list[100];
  sprintf(list, "listener: port %i", port);
  xreg(fd, list);
  int yes = 1;
  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);

  this_addr = InAddr::listener(af, port);

  if (bind(fd, this_addr->addr(), this_addr->addr_len())!=0) {
      log(PFL_SEEINFO, 0, "socket", "port %i %s : not accepting connections", port, strerror(errno));
      dead = true;
      return;
  };

  this_addr = InAddr::getsockname(fd);

  listen(fd, 10);
  fcntl(fd, F_SETFL, O_NONBLOCK);
  dead = false;
  xselect(fd);
}

int Socket::connect(int fd) {
    this->port = -1;
    this->fd = fd;
    this->dead = 0;
    that_addr = InAddr::getpeername(fd);
    this_addr = InAddr::getsockname(fd);
    return 0;
}

int Socket::connect(const char *where, int port) {
    this->port = -1;

    struct sockaddr_in iaddr;
    iaddr.sin_family = AF_INET;
    iaddr.sin_port = htons(port);
    inet_aton(where, &iaddr.sin_addr);
    
    fd = socket(AF_INET, SOCK_STREAM, 0);
    xreg(fd, "outgoing connection");
    xselect(fd);
    int value = ::connect(fd, (struct sockaddr *)&iaddr, sizeof iaddr);
    
    int er = errno;
    
    if (value == -1) {
	xunreg(fd);
	close(fd);
	return -er;
    }
    
    that_addr = InAddr::getpeername(fd);
    this_addr = InAddr::getsockname(fd);

    fcntl(fd, F_SETFL, O_NONBLOCK);
    dead = false;
    
    return true;
}


Socket *Socket::accept() {
  int sockfd;
  struct sockaddr_storage addr;
  socklen_t size = sizeof addr;

  sockfd = ::accept(fd, (struct sockaddr *)&addr, &size);

  if (sockfd != -1) {

    Socket *sock = new Socket();
    sock->fd = sockfd;
    xreg(sockfd, "incoming connection");
    xselect(sockfd);
    fcntl (sockfd, F_SETFL, O_NONBLOCK);
    sock->this_addr = InAddr::getsockname(sockfd);
    sock->that_addr = InAddr::getpeername(sockfd);
    if (sock_debug)
      printf("accept(%i) = %i (from %s)\n", fd, sockfd, sock->that_addr->tostring().c_str());
    sock->dead = false;
    return sock;

  }
  return 0;
}

int Socket::read(char *data, int size) {
  if (dead) {
      errno = ECONNRESET;
      return -1;
  }
  int rval = ::read(fd, data, size);
  if (rval == -1) {
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
	  return -1;
      }
      dead = true;
  }
  if (sock_debug)
    printf("read(%i) == \"%s\"\n", fd, esc(data, rval).c_str());
  return rval;
}

int Socket::write(const char *data, int size) {
    if (dead) {
	errno = ECONNRESET;
	return -1;
    }
    int rval = ::write(fd, data, size);
    if (sock_debug)
      printf("write(%i, \"%s\") == %i\n", fd, esc(data, size).c_str(), rval);
    if (rval == -1) {
	dead = true;
    }
    return rval;
}

Socket::~Socket() {
  if (sock_debug)
    printf("close(%i)\n", fd);
  delete this_addr;
  delete that_addr;
  close(fd);
  xunreg(fd);
}

InAddr *Socket::get_addr()
{
  return that_addr;
}

InAddr *Socket::get_local_addr()
{
  return this_addr;
}

string Socket::read_data() 
{
  string s;
  while (1) {
    char c;
    errno = 0;
    if (::read(fd, &c, 1)==1)
      s += c;
    else {
      if (errno != EAGAIN &&
	  errno != EWOULDBLOCK)
	dead = 1;
      return s;
    }
  }
}