#include "ip++.h"
#include "hosts.h"
#include <security_utilities/debugging.h>
#include <arpa/inet.h>
#include <netdb.h>
namespace Security {
namespace IPPlusPlus {
typedef unsigned char Byte;
static const struct in_addr in_addr_any = { INADDR_ANY };
#if BUG_GCC
const IPAddress &IPAddress::any = *static_cast<const IPAddress *>(&in_addr_any);
#else
const IPAddress &IPAddress::any = static_cast<const IPAddress &>(in_addr_any);
#endif
IPAddress::IPAddress(const char *s)
{
if (!inet_aton(s, this))
UnixError::throwMe(EINVAL);
}
IPAddress::operator string() const
{
const Byte *p = reinterpret_cast<const Byte *>(this);
char buffer[(3+1)*4]; snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
return buffer;
}
IPSockAddress::IPSockAddress()
{
sin_family = AF_INET;
}
IPSockAddress::IPSockAddress(const IPAddress &addr, IPPort port)
{
sin_family = AF_INET;
sin_addr = addr;
sin_port = htons(port);
}
IPSockAddress::operator string () const
{
char buffer[4*(3+1)+5+1]; snprintf(buffer, sizeof(buffer), "%s:%d", string(address()).c_str(), port());
return buffer;
}
IPSockAddress IPSockAddress::defaults(const IPSockAddress &defaultAddr) const
{
return defaults(defaultAddr.address(), defaultAddr.port());
}
IPSockAddress IPSockAddress::defaults(const IPAddress &defaultAddr, IPPort defaultPort) const
{
return IPSockAddress(
address() ? address() : defaultAddr,
port() ? port() : defaultPort
);
}
IPSockAddress IPSockAddress::defaults(IPPort defaultPort) const
{
return IPSockAddress(address(), port() ? port() : defaultPort);
}
UNSockAddress::UNSockAddress()
{
sun_family = AF_UNIX;
}
UNSockAddress::UNSockAddress(const char *path)
{
sun_family = AF_UNIX;
size_t length = strlen(path);
if (length >= sizeof(sun_path)) UnixError::throwMe(EINVAL);
memcpy(sun_path, path, length + 1);
}
UNSockAddress::UNSockAddress(const string &path)
{
sun_family = AF_UNIX;
if (path.length() >= sizeof(sun_path)) UnixError::throwMe(EINVAL);
memcpy(sun_path, path.c_str(), path.length() + 1);
}
string UNSockAddress::path() const
{
return sun_path;
}
Socket::Socket(int type)
{
open(type);
}
Socket::Socket(int domain, int type, int protocol)
{
open(domain, type, protocol);
}
void Socket::open(int domain, int type, int protocol)
{
checkSetFd(::socket(domain, type, protocol));
mAtEnd = false;
secdebug("sockio", "socket(%d,%d) -> %d", type, protocol, fd());
}
void Socket::prepare(int fdFlags, int domain, int type, int protocol)
{
if (!isOpen())
open(domain, type, protocol);
if (fdFlags)
setFlag(fdFlags);
}
void Socket::bind(const IPAddress &addr, IPPort port)
{
bind(IPSockAddress(addr, port));
}
void Socket::bind(const IPSockAddress &local)
{
checkError(::bind(fd(), local, sizeof(local)));
secdebug("sockio", "%d bind to %s", fd(), string(local).c_str());
}
void Socket::bind(const UNSockAddress &local)
{
checkError(::bind(fd(), local, sizeof(local)));
secdebug("sockio", "%d bind to %s", fd(), string(local).c_str());
}
void Socket::listen(int backlog)
{
checkError(::listen(fd(), backlog));
}
void Socket::accept(Socket &s)
{
IPSockAddress dummy; return accept(s, dummy);
}
void Socket::accept(Socket &s, IPSockAddress &peer)
{
socklen_t length = sizeof(IPSockAddress);
s.checkSetFd(::accept(fd(), peer, &length));
assert(length == sizeof(IPSockAddress));
}
void Socket::accept(Socket &s, UNSockAddress &peer)
{
socklen_t length = sizeof(UNSockAddress);
s.checkSetFd(::accept(fd(), peer, &length));
assert(length == sizeof(UNSockAddress));
}
bool Socket::connect(const IPSockAddress &peer)
{
if (::connect(fd(), peer, sizeof(peer))) {
switch (errno) {
case EINPROGRESS:
secdebug("sockio", "%d connecting to %s", fd(), string(peer).c_str());
return false;
case EALREADY:
if (int err = error()) UnixError::throwMe(err);
secdebug("sockio", "%d still trying to connect", fd());
return false;
case EISCONN:
if (flags() & O_NONBLOCK) {
secdebug("sockio", "%d now connected", fd());
return true;
} else {
UnixError::throwMe();
}
default:
UnixError::throwMe();
}
} else {
secdebug("sockio", "%d connect to %s", fd(), string(peer).c_str());
return true;
}
}
bool Socket::connect(const IPAddress &addr, IPPort port)
{
return connect(IPSockAddress(addr, port));
}
bool Socket::connect(const UNSockAddress &peer)
{
checkError(::connect(fd(), peer, sizeof(peer)));
secdebug("sockio", "%d connect to %s", fd(), string(peer).c_str());
return true;
}
void Socket::shutdown(int how)
{
assert(how >= 0 && how <= 2);
checkError(::shutdown(fd(), how));
}
IPSockAddress Socket::localAddress() const
{
IPSockAddress addr;
socklen_t length = sizeof(addr);
checkError(::getsockname(fd(), addr, &length));
assert(length == sizeof(addr));
return addr;
}
IPSockAddress Socket::peerAddress() const
{
IPSockAddress addr;
socklen_t length = sizeof(addr);
checkError(::getpeername(fd(), addr, &length));
assert(length == sizeof(addr));
return addr;
}
void Socket::getOption(void *value, socklen_t &length, int name, int level ) const
{
UnixError::check(::getsockopt(fd(), level, name, value, &length));
}
void Socket::setOption(const void *value, int length, int name, int level ) const
{
UnixError::check(::setsockopt(fd(), level, name, value, length));
}
void Socket::connect(const Host &host, IPPort port)
{
set<IPAddress> addrs = host.addresses();
for (set<IPAddress>::const_iterator it = addrs.begin(); it != addrs.end(); it++) {
const IPSockAddress address(*it, port);
if (::connect(fd(), address, sizeof(IPSockAddress)) == 0) {
secdebug("sockio", "%d connect to %s", fd(), string(address).c_str());
return;
}
}
UnixError::throwMe();
}
void TCPClientSocket::open(const IPSockAddress &peer, int fdFlags)
{
prepare(fdFlags, AF_INET, SOCK_STREAM);
connect(peer);
}
void TCPClientSocket::open(const IPAddress &addr, IPPort port, int fdFlags)
{
prepare(fdFlags, AF_INET, SOCK_STREAM);
connect(addr, port);
}
void TCPClientSocket::open(const Host &host, IPPort port, int fdFlags)
{
prepare(fdFlags, AF_INET, SOCK_STREAM);
connect(host, port);
}
TCPClientSocket::~TCPClientSocket()
{
close();
}
void TCPServerSocket::open(const IPSockAddress &addr, int depth)
{
prepare(0, AF_INET, SOCK_STREAM);
bind(addr);
listen(depth);
}
void TCPServerSocket::operator () (TCPClientSocket &newClient)
{
accept(newClient);
}
void TCPServerSocket::receive(TCPClientSocket &newClient)
{
accept(newClient);
close();
}
TCPServerSocket::~TCPServerSocket()
{
close();
}
} }