// ---------------------------------------------------------------------------
// - Socket.cpp -
// - afnix:net module - socket class implementation -
// ---------------------------------------------------------------------------
// - This program is free software; you can redistribute it and/or modify -
// - it provided that this copyright notice is kept intact. -
// - -
// - 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. In no event shall -
// - the copyright holder be liable for any direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software. -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2007 amaury darsch -
// ---------------------------------------------------------------------------
#include "Item.hpp"
#include "Socket.hpp"
#include "Vector.hpp"
#include "Integer.hpp"
#include "Boolean.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "cnet.hpp"
#include "csio.hpp"
#include "cerr.hpp"
namespace afnix {
// -------------------------------------------------------------------------
// - private section -
// -------------------------------------------------------------------------
// the socket eval quarks
static const long QUARK_REUSEADDR = String::intern ("REUSE-ADDRESS");
static const long QUARK_BROADCAST = String::intern ("BROADCAST");
static const long QUARK_DONTROUTE = String::intern ("DONT-ROUTE");
static const long QUARK_KEEPALIVE = String::intern ("KEEP-ALIVE");
static const long QUARK_LINGER = String::intern ("LINGER");
static const long QUARK_RCVSIZE = String::intern ("RCV-SIZE");
static const long QUARK_SNDSIZE = String::intern ("SND-SIZE");
static const long QUARK_HOPLIMIT = String::intern ("HOP-LIMIT");
static const long QUARK_MCASTLOOP = String::intern ("MULTICAST-LOOPBACK");
static const long QUARK_MCASTHOP = String::intern ("MULTICAST-HOP-LIMIT");
static const long QUARK_MAXSEG = String::intern ("MAX-SEGMENT-SIZE");
static const long QUARK_NODELAY = String::intern ("NO-DELAY");
static const long QUARK_SOCKET = String::intern ("Socket");
// map an enumeration item to a socket option
static inline Socket::t_option item_to_option (const Item& item) {
// check for a socket item
if (item.gettid () != QUARK_SOCKET)
throw Exception ("item-error", "item is not an socket item");
// map the item to the enumeration
long quark = item.getquark ();
if (quark == QUARK_REUSEADDR) return Socket::SOCK_REUSEADDR;
if (quark == QUARK_BROADCAST) return Socket::SOCK_BROADCAST;
if (quark == QUARK_DONTROUTE) return Socket::SOCK_DONTROUTE;
if (quark == QUARK_KEEPALIVE) return Socket::SOCK_KEEPALIVE;
if (quark == QUARK_LINGER) return Socket::SOCK_LINGER;
if (quark == QUARK_RCVSIZE) return Socket::SOCK_RCVSIZE;
if (quark == QUARK_SNDSIZE) return Socket::SOCK_SNDSIZE;
if (quark == QUARK_HOPLIMIT) return Socket::SOCK_HOPLIMIT;
if (quark == QUARK_MCASTLOOP) return Socket::SOCK_MCASTLOOP;
if (quark == QUARK_MCASTHOP) return Socket::SOCK_MCASTHOP;
if (quark == QUARK_MAXSEG) return Socket::SOCK_MAXSEG;
if (quark == QUARK_NODELAY) return Socket::SOCK_NODELAY;
throw Exception ("item-error", "cannot map item to socket option");
}
// -------------------------------------------------------------------------
// - class section -
// -------------------------------------------------------------------------
// create a default socket
Socket::Socket (void) {
d_sid = -1;
}
// create a socket by id
Socket::Socket (const int sid) {
d_sid = sid;
}
// destroy this socket
Socket::~Socket (void) {
close ();
}
// return the class name
String Socket::repr (void) const {
return "Socket";
}
// return true if we have an ipv6 socket
bool Socket::isipv6 (void) const {
rdlock ();
bool result = c_isipv6 (d_sid);
unlock ();
return result;
}
// return true if we can broadcast message
bool Socket::isbcast (void) const {
rdlock ();
bool result = false;
unlock ();
return result;
}
// return true if the socket is open
bool Socket::isopen (void) const {
rdlock ();
bool status = (d_sid != -1);
unlock ();
return status;
}
// close this socket
bool Socket::close (void) {
wrlock ();
if ((d_sid == -1) || (Object::uref (this) == false)) {
unlock ();
return true;
}
if (c_close (d_sid) == false) {
unlock ();
return false;
}
d_sid = -1;
unlock ();
return true;
}
// force a socket shutdown
bool Socket::shutdown (void) {
wrlock ();
if (d_sid == -1) {
unlock ();
return true;
}
if (c_close (d_sid) == false) {
unlock ();
return false;
}
d_sid = -1;
unlock ();
return true;
}
// shutdown this socket
bool Socket::shutdown (const bool mode) {
wrlock ();
bool result = false;
if (mode == false) result = c_ipshut (d_sid, SOCKET_SHUT_RECV);
if (mode == true) result = c_ipshut (d_sid, SOCKET_SHUT_SEND);
unlock ();
return result;
}
// set a socket option
bool Socket::setopt (t_option opt, bool flag) {
wrlock ();
bool result = false;
switch (opt) {
case SOCK_REUSEADDR:
result = c_ipsetopt (d_sid, SOCKET_REUSEADDR, flag, 0);
break;
case SOCK_BROADCAST:
result = c_ipsetopt (d_sid, SOCKET_BROADCAST, flag, 0);
break;
case SOCK_DONTROUTE:
result = c_ipsetopt (d_sid, SOCKET_DONTROUTE, flag, 0);
break;
case SOCK_KEEPALIVE:
result = c_ipsetopt (d_sid, SOCKET_KEEPALIVE, flag, 0);
break;
case SOCK_MCASTLOOP:
result = c_ipsetopt (d_sid, SOCKET_MCASTLOOP, flag, 0);
break;
case SOCK_NODELAY:
result = c_ipsetopt (d_sid, SOCKET_NODELAY, flag, 0);
break;
default:
break;
}
unlock ();
return result;
}
// set a socket option with a value
bool Socket::setopt (t_option opt, bool flag, long val) {
wrlock ();
bool result = false;
switch (opt) {
case SOCK_LINGER:
result = c_ipsetopt (d_sid, SOCKET_LINGER, flag, val);
break;
case SOCK_RCVSIZE:
result = c_ipsetopt (d_sid, SOCKET_RCVSIZE, flag, val);
break;
case SOCK_SNDSIZE:
result = c_ipsetopt (d_sid, SOCKET_SNDSIZE, flag, val);
break;
case SOCK_HOPLIMIT:
result = c_ipsetopt (d_sid, SOCKET_HOPLIMIT, flag, val);
break;
case SOCK_MCASTHOP:
result = c_ipsetopt (d_sid, SOCKET_MCASTHOP, flag, val);
break;
case SOCK_MAXSEG:
result = c_ipsetopt (d_sid, SOCKET_MAXSEG, flag, val);
break;
default:
break;
}
unlock ();
return result;
}
// connect this socket by port and address
bool Socket::connect (t_word port, const Address& addr) {
rdlock ();
bool result = c_ipconnect (d_sid, port, addr.p_addr);
unlock ();
return result;
}
// connect this socket by port and aliases address
bool Socket::connect (t_word port, const Address& addr, const bool alsf) {
wrlock ();
addr.rdlock ();
// start with canonical address
bool status = c_ipconnect (d_sid, port, addr.p_addr);
if ((status == true) || (alsf == false)) {
addr.unlock ();
unlock ();
return status;
}
// use aliases address mode
for (long i = 0; i < addr.d_size; i++) {
status = c_ipconnect (d_sid, port, addr.p_aals[i].p_aadr);
if (status == true) {
addr.unlock ();
unlock ();
return true;
}
}
// connection failure
addr.unlock ();
unlock ();
return false;
}
// bind a socket with a port
bool Socket::bind (t_word port) {
rdlock ();
bool result = c_ipbind (d_sid, port);
unlock ();
return result;
}
// bind a socket with a port and an address
bool Socket::bind (t_word port, const Address& addr) {
rdlock ();
addr.rdlock ();
bool result = c_ipbind (d_sid, port, addr.p_addr);
addr.unlock ();
unlock ();
return result;
}
// return the socket address
Address* Socket::getsockaddr (void) const {
rdlock ();
t_byte* addr = c_ipsockaddr (d_sid);
unlock ();
if (addr == nilp) return nilp;
Address* result = new Address (addr);
delete [] addr;
return result;
}
// return the socket port
t_word Socket::getsockport (void) const {
rdlock ();
t_word result = c_ipsockport (d_sid);
unlock ();
return result;
}
// return the peer address
Address* Socket::getpeeraddr (void) const {
rdlock ();
t_byte* addr = c_ippeeraddr (d_sid);
unlock ();
if (addr == nilp) return nilp;
Address* result = new Address (addr);
delete [] addr;
return result;
}
// return the peer port
t_word Socket::getpeerport (void) const {
rdlock ();
t_word result = c_ippeerport (d_sid);
unlock ();
return result;
}
// -------------------------------------------------------------------------
// - object section -
// -------------------------------------------------------------------------
// the quark zone
static const long QUARK_ZONE_LENGTH = 18;
static QuarkZone zone (QUARK_ZONE_LENGTH);
// the object supported quarks
static const long QUARK_BIND = zone.intern ("bind");
static const long QUARK_CLOSE = zone.intern ("close");
static const long QUARK_OPENP = zone.intern ("open-p");
static const long QUARK_IPV6P = zone.intern ("ipv6-p");
static const long QUARK_BCASTP = zone.intern ("broadcast-p");
static const long QUARK_SETOPT = zone.intern ("set-option");
static const long QUARK_CONNECT = zone.intern ("connect");
static const long QUARK_SETEMOD = zone.intern ("set-encoding-mode");
static const long QUARK_SHUTDOWN = zone.intern ("shutdown");
static const long QUARK_SOCKADDR = zone.intern ("get-socket-address");
static const long QUARK_SOCKPORT = zone.intern ("get-socket-port");
static const long QUARK_PEERADDR = zone.intern ("get-peer-address");
static const long QUARK_PEERPORT = zone.intern ("get-peer-port");
static const long QUARK_SETOPTION = zone.intern ("set-option");
static const long QUARK_SETIEMOD = zone.intern ("set-input-encoding-mode");
static const long QUARK_GETIEMOD = zone.intern ("get-input-encoding-mode");
static const long QUARK_SETOEMOD = zone.intern ("set-output-encoding-mode");
static const long QUARK_GETOEMOD = zone.intern ("get-output-encoding-mode");
// local quarks
static const long QUARK_GETEMOD = String::intern ("get-encoding-mode");
// return true if the given quark is defined
bool Socket::isquark (const long quark, const bool hflg) const {
rdlock ();
if (zone.exists (quark) == true) {
unlock ();
return true;
}
bool result = hflg ? Input::isquark (quark, hflg) : false;
if (result == false) result = hflg ? Output::isquark (quark, hflg) : false;
unlock ();
return result;
}
// evaluate a quark statically
Object* Socket::meval (Runnable* robj, Nameset* nset, const long quark) {
if (quark == QUARK_REUSEADDR)
return new Item (QUARK_SOCKET, QUARK_REUSEADDR);
if (quark == QUARK_BROADCAST)
return new Item (QUARK_SOCKET, QUARK_BROADCAST);
if (quark == QUARK_DONTROUTE)
return new Item (QUARK_SOCKET, QUARK_DONTROUTE);
if (quark == QUARK_MCASTLOOP)
return new Item (QUARK_SOCKET, QUARK_MCASTLOOP);
if (quark == QUARK_KEEPALIVE)
return new Item (QUARK_SOCKET, QUARK_KEEPALIVE);
if (quark == QUARK_LINGER)
return new Item (QUARK_SOCKET, QUARK_LINGER);
if (quark == QUARK_RCVSIZE)
return new Item (QUARK_SOCKET, QUARK_RCVSIZE);
if (quark == QUARK_SNDSIZE)
return new Item (QUARK_SOCKET, QUARK_SNDSIZE);
if (quark == QUARK_HOPLIMIT)
return new Item (QUARK_SOCKET, QUARK_HOPLIMIT);
if (quark == QUARK_MCASTHOP)
return new Item (QUARK_SOCKET, QUARK_MCASTHOP);
if (quark == QUARK_MAXSEG)
return new Item (QUARK_SOCKET, QUARK_MAXSEG);
if (quark == QUARK_NODELAY)
return new Item (QUARK_SOCKET, QUARK_NODELAY);
throw Exception ("eval-error", "cannot evaluate member",
String::qmap (quark));
}
// apply this object with a set of arguments and a quark
Object* Socket::apply (Runnable* robj, Nameset* nset, const long quark,
Vector* argv) {
// check the special quark first
if (quark == QUARK_SETIEMOD) {
return Input::apply (robj, nset, QUARK_SETEMOD, argv);
}
if (quark == QUARK_GETIEMOD) {
return Input::apply (robj, nset, QUARK_GETEMOD, argv);
}
if (quark == QUARK_SETOEMOD) {
return Output::apply (robj, nset, QUARK_SETEMOD, argv);
}
if (quark == QUARK_GETOEMOD) {
return Output::apply (robj, nset, QUARK_GETEMOD, argv);
}
if (quark == QUARK_SETEMOD) {
Object::cref (Input::apply (robj, nset, quark, argv));
Object::cref (Output::apply (robj, nset, quark, argv));
return nilp;
}
// get the number of arguments
long argc = (argv == nilp) ? 0 : argv->length ();
// dispatch 0 argument
if (argc == 0) {
if (quark == QUARK_IPV6P) return new Boolean (isipv6 ());
if (quark == QUARK_OPENP) return new Boolean (isopen ());
if (quark == QUARK_BCASTP) return new Boolean (isbcast ());
if (quark == QUARK_SOCKPORT) return new Integer (getsockport ());
if (quark == QUARK_PEERPORT) return new Integer (getpeerport ());
if (quark == QUARK_CLOSE) return new Boolean (close ());
if (quark == QUARK_SHUTDOWN) return new Boolean (shutdown ());
if (quark == QUARK_SOCKADDR) {
rdlock ();
try {
Object* result = getsockaddr ();
robj->post (result);
unlock ();
return result;
} catch (...) {
unlock ();
throw;
}
}
if (quark == QUARK_PEERADDR) {
rdlock ();
try {
Object* result = getpeeraddr ();
robj->post (result);
unlock ();
return result;
} catch (...) {
unlock ();
throw;
}
}
}
// dispatch 1 argument
if (argc == 1) {
if (quark == QUARK_BIND) {
long port = argv->getint (0);
bind (port);
return nilp;
}
if (quark == QUARK_SHUTDOWN) {
bool mode = argv->getbool (0);
return new Boolean (shutdown (mode));
}
}
// dispatch 2 arguments
if (argc == 2) {
if (quark == QUARK_BIND) {
long port = argv->getint (0);
Address* addr = dynamic_cast <Address*> (argv->get (1));
if (addr == nilp)
throw Exception ("argument-error", "address expected with bind");
bind (port, *addr);
return nilp;
}
if (quark == QUARK_CONNECT) {
long port = argv->getint (0);
Address* addr = dynamic_cast <Address*> (argv->get (1));
if (addr == nilp)
throw Exception ("argument-error", "address expected with connect");
connect (port, *addr);
return nilp;
}
if (quark == QUARK_SETOPTION) {
Item* item = dynamic_cast <Item*> (argv->get (0));
if (item == nilp) throw Exception ("argument-error",
"invalid object as socket option");
t_option opt = item_to_option (*item);
Object* obj = argv->get (1);
Boolean* bobj = dynamic_cast <Boolean*> (obj);
if (bobj != nilp) {
bool flg = bobj->toboolean ();
return new Boolean (setopt (opt, flg));
}
Integer* iobj = dynamic_cast <Integer*> (obj);
if (iobj != nilp) {
long val = iobj->tointeger ();
return new Boolean (setopt (opt, true, val));
}
throw Exception ("argument-error", "invalid argument with set-option");
}
}
// dispatch 3 arguments
if (argc == 3) {
if (quark == QUARK_SETOPTION) {
Item* item = dynamic_cast <Item*> (argv->get (0));
if (item == nilp) throw Exception ("argument-error",
"invalid object as socket option");
t_option opt = item_to_option (*item);
bool flg = argv->getbool (1);
long val = argv->getint (2);
return new Boolean (setopt (opt, flg, val));
}
if (quark == QUARK_CONNECT) {
long port = argv->getint (0);
Address* addr = dynamic_cast <Address*> (argv->get (1));
if (addr == nilp)
throw Exception ("argument-error", "address expected with connect");
bool alsf = argv->getbool (2);
connect (port, *addr, alsf);
return nilp;
}
}
// check for input stream quark
if (Input::isquark (quark, true) == true)
return Input::apply (robj, nset, quark, argv);
// check for output stream quark
if (Output::isquark (quark, true) == true)
return Output::apply (robj, nset, quark, argv);
// call the object method
return Object::apply (robj, nset, quark, argv);
}
}
syntax highlighted by Code2HTML, v. 0.9.1