/* * 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; } } }