/* * The Unix Channel * * by Michel Beaudouin-Lafon * * Copyright 1990-1997 * Laboratoire de Recherche en Informatique (LRI) * * Sockets * * $Id$ * $CurLog$ */ #include "Socket.h" #include #include #include extern int errno; /*?class UchSocket The class \typ{UchSocket} derives from \typ{UchChannel}. It is a virtual base class: no objects of class \typ{UchSocket} are ever created. It implements Unix sockets, that is file descriptors with two addresses: the address the socket is bound to, and the address it is connected to. An address needs to be bound to a socket only if another process wants to connect to this socket; a socket needs to be connected to an address only for streams or connected datagrams. Thus, none of the addresses is mandatory. A socket can have potentially two addresses (the one it is BoundTo and the one it is ConnectedTo. Once given to a socket, an \typ{UchAddress} instance pointer is no more usable because it can be deleted and reallocated by the Socket when it is bound/connected. These address pointers are also deleted by the socket destructor. Thus, to know the address of a socket always use \fun{BoundTo} and \fun{ConnectedTo}. ?*/ /*? Construct a closed socket bound to address \var{bound} and connected to address \var{connected}. Each or both arguments may be 0. These addresses have to be freshly allocated objects and will be deleted by the socket's destructor so it's not necessary (and dangerous) to delete them. always allocated by new. Theses addresses are deleted and reinstanciated after a \fun{Bind}. To obtain the addresses of a socket, use \fun{BoundTo} and \fun{ConnectedTo}. ?*/ UchSocket :: UchSocket (UchAddress* bound, UchAddress* connected) : UchChannel (), BAddr (bound), CAddr (connected), AddrFamily (AF_UNSPEC), Ready (false) { } /*?nodoc?*/ UchSocket :: UchSocket (const UchSocket& s) : UchChannel (s), BAddr (s.BAddr), CAddr (s.CAddr), Ready (s.Ready) { } /*?nodoc?*/ UchSocket :: ~UchSocket () { if (BAddr) delete BAddr; if (CAddr) delete CAddr; } #ifdef DOC /*? This virtual function returns the type of the socket: one of \var{SOCK\_UNSPEC}, \var{SOCK\_STREAM}, \var{SOCK\_DGRAM}, depending on the class. ?*/ int UchSocket :: SockType () { } #endif /*?nextdoc?*/ void UchSocket :: BindTo (UchAddress* a) { if (BAddr) delete BAddr; BAddr = a; } /*? Set the address a socket is to be bound to or connected to. ?*/ void UchSocket :: ConnectTo (UchAddress* a) { if (CAddr) delete CAddr; CAddr = a; } /*? Open the socket (with the \fun{socket} system call) if it is not already open. The socket must have an address bound or connected to get the protocol family, or its family must have been defined with \fun{SetFamily}. Return false if the family is undefined or if a system error occurred. ?*/ bool UchSocket :: Open () { errno = 0; if (Fd >= 0) return true; if (AddrFamily == AF_UNSPEC) { if (BAddr) AddrFamily = BAddr->Family (); else if (CAddr) AddrFamily = CAddr->Family (); else return false; } int fd = socket (AddrFamily, SockType (), 0); if (fd < 0) return false; UchChannel::Open (fd); return true; } /*?nextdoc?*/ int UchSocket :: Bind (UchAddress* addr) { if (addr) BindTo (addr); if (! BAddr || ! BAddr->IsValid ()) return -1; if (! Open ()) return -1; int ret = bind (Fd, BAddr->GetSockAddr (), BAddr->Length ()); if (ret < 0) return ret; GEN_ADDR naddr; int alen = sizeof (naddr); if (getsockname (Fd, &naddr.sa, &alen) < 0) return -1; BindTo (UchAddress::Decode (&naddr, alen)); return ret; } /*? These two functions implement the Unix system calls \fun{bind} and \fun{connect}. If \var{addr} is given, it is first associated to the socket. Then the socket is opened, and finally the system call is performed. The returned value is that of the system call, unless opening failed in which case -1 is returned. ?*/ int UchSocket :: Connect (UchAddress* addr) { if (addr) ConnectTo (addr); if (! CAddr || ! CAddr->IsValid ()) return -1; if (! Open ()) return -1; int ret = connect (Fd, CAddr->GetSockAddr (), CAddr->Length ()); if (ret < 0) return ret; GEN_ADDR naddr; int alen = sizeof (naddr); if (getpeername (Fd, &naddr.sa, &alen) < 0) return -1; ConnectTo (UchAddress::Decode (&naddr, alen)); return ret; } /*? Open the socket if it is not already open. Bind and connect it depending on the addresses that are defined. You should use this function instead of calling \fun{Open}, \fun{Bind} and \fun{Connect}, for it is simpler. This function returns false if a system error occurred. In this case, the caller can call \fun{SysError} to report the error. ?*/ bool UchSocket :: Setup () { if (! Open ()) return Ready = false; if (BAddr && BAddr->IsValid ()) if (Bind () < 0) return Ready = false; if (CAddr && CAddr->IsValid ()) if (Connect () < 0) return Ready = false; return Ready = true; } /*? This function is intended for sockets that accept connections. It returns the file descriptor of the new connection, or -1 in case of failure. ?*/ int UchSocket :: Accept () { errno = 0; return accept (Fd, 0, 0); } bool UchSocket :: ReuseAddress (bool on) { int parm = on; int res = setsockopt (Fd, SOL_SOCKET, SO_REUSEADDR, (char *)&parm, sizeof (parm)); return res < 0 ? false : true; } bool UchSocket :: AllowBroadcast (bool on) { int parm = on; int res = setsockopt (Fd, SOL_SOCKET, SO_BROADCAST, (char *)&parm, sizeof (parm)); return res < 0 ? false : true; } /*?nodoc?*/ char* UchSocket :: StrRepr (char* buf) { UchChannel :: StrRepr (buf); strcat (buf, " / "); if (BAddr) BAddr->StrRepr (buf + strlen (buf)); else strcat (buf, "null"); strcat (buf, " / "); if (CAddr) CAddr->StrRepr (buf + strlen (buf)); else strcat (buf, "null"); return buf; } #ifdef DOC /*?nextdoc?*/ int UchSocket :: Family () { } /*? These function get and set the family. The possible values currently are \var{AF\_UNSPEC}, \var{AF\_UNIX} and \var{AF\_INET}. Other values may be defined if supported by the system. The family must be defined for \fun{Open} (and thus \fun{Setup}) to succeed. If an address is bound or connected to the socket, its family is used. Thus, an application seldom needs to call \fun{SetFamily}. ?*/ void UchSocket :: SetFamily (int f) { } /*?nextdoc?*/ UchAddress* UchSocket :: BoundTo () { } /*? Return the address currently bound or connected to the socket. ?*/ UchAddress* UchSocket :: ConnectedTo () { } #endif /* DOC */