/* * Copyright (c) 1996 Gunther Schadow. All rights reserved. * * 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. */ #ifndef _SOCKET_H #define _SOCKET_H #pragma interface #include #include #include #include #ifndef MSG_EOF # define MSG_EOF (MSG_DONTROUTE << 1); #else # define TCP_MSG_EOF #endif #ifndef MSG_EOR # define MSG_EOR (MSG_DONTROUTE << 2); #endif /* addrtok() -- parse an address step An address has the following form: { [ "(" [ ] ")" ] ":" } BNF: address := step | step ":" address; step := type | "(" ")" | type "(" parameters ")"; Examples: HLLP:TCP(host.domain:service) UDP(host.domain:service) ANSI-X3.28(options):TTY(/dev/cua00) FILE(path) ILLP:PIPE(shell command) UNIX-DGRAM(path) MLLP:UNIX-STREAM(path) MLLP:FIFO(path) The function addrtok() returns the next step of the address or NULL if it was the final step. If the type argument is non null it will point to the type of this step. If type_len is non null it is set to the length of the type. If param is non null it will point to the parameter string if param_len is non null it is set to the length of the parameter string. Parsing the parameter string is the rsponsibility of the class which is registered for the address type. */ const char *addrtok(const char *address, const char **type = NULL, size_t *type_len = NULL, const char **param = NULL, size_t *param_len = NULL); /* Socket like objects make up a chain where the head of the chain normally is a proper Socket object. The capabilities of the Socket chain as a whole depends on the capabilities of each of its members. The capabilities are defined at the time of construction of the chain and remain constant during the lifetime of the chain. These include the following: full_duplex - symmetric communication (like telephone) half_duplex - assymetric communication (like CB-radio) any_duplex - either full or half duplex (union of both). No duplex bit is set (=simplex mode) if only one way communication is allowed (e.g. a plain file) multiplex - can handle several threads (like BSD sockets) send_mode - can send recv_mode - can receive send_or_recv - can send or receive (union of both) accept_able - can accept, e.g. work in a proper server mode Each member may add capabilities to the list if they can guarantee for their validity. E.g. a Socket like class that combines two files can add the duplex capability even though, each file can only work in simplex mode. Likewise, a socket must remove a capabilities from the list, if they cannot work in that mode. E.g. a special protocol (like ANSI X3.28) may only work in half duplex mode, even though the underlying device used is full duplex (like a BSD socket). The state is different from the capabilities in that it may change during the lifetime of the chain. E.g. a simplex socket will decide wether it is recv_able or send_able when a connection is made. There are state flags which are also used as capabilities (e.g. send_able) and others that are only used as state flags. The states are as follows: send_mode - can send recv_mode - can receive send_or_recv - can send or receive (union of both) connected - is properly connected receiving - is in receiving mode (for half_duplex) sending - is in sending mode (for half_duplex) eom_seen - has got an end of message EOM token eot_seen - has got an end of transmission EOT token error - has an error condition */ class Socket { public: Socket(); Socket(Socket *); Socket(const char *address); virtual ~Socket(); // capabilities and states enum mode_flags { # define BIT(n) (1 << n) leafnode = BIT(0), // this is a final level (no lower level) // capabilities full_duplex = BIT(1), // symmetric communication (like telephone) half_duplex = BIT(2), // assymetric communication (like CB-radio) any_duplex = ( half_duplex | full_duplex ), accept_able = BIT(3), // can accept otherwise must poll (connect) multiplex = BIT(4), // several threads per line (like BSD socket) // capabilities and also states async_mode = BIT(5), // asyncronous mode (not yet reliably supported) noblock_mode = BIT(6), // noblock mode (not yet reliably supported) send_mode = BIT(7), // (can) send recv_mode = BIT(8), // (can) receive send_recv = (send_mode | recv_mode), // states connected = BIT(9), receiving = BIT(10), sending = BIT(11), eom_seen = BIT(12), eot_seen = BIT(13), eom_placed = BIT(14), eot_placed = BIT(15), eox_any = eom_seen | eom_placed | eot_seen | eot_placed, error = BIT(16), // special mode for close() silently = BIT(17), // like SO_KEEPALIVE? any_capabilitiy = ( any_duplex | multiplex | accept_able | async_mode | noblock_mode | send_recv ), any_state = ( async_mode | noblock_mode | send_recv | connected | sending | receiving | eom_seen | error ), // A state that is desireable but not yet supported: // for buffering protocols, means they shouldn't buffer dontbuffer = BIT(18), }; // flags for send() and recv() enum msg_flags { msg_eom = MSG_EOF, // if send() is called with the msg_eom flag set, it places an // end-of-message (EOM) token onto the stream, after having sent // the data. By default, EOM should be implemented as a shutdown // on the write end. Thus, raw stream protocols (like TCP) will // transport at most two messages: (1) request, (2) reply before // the socket becomes unusable. Higher level protocols can // implement a different method of passing an EOM token and thus // can handle more messages on a single connection. The eom_msg // flag is usually masked out when the send() call is forwarded // down the level chain. // If an EOM token is read, it is handed to upper layers as a // temporary end-of-file (EOF) condition (i.e. recv() returns 0 // after all data has been read). However, after the eom_seen flag // has been cleared with setflag(0, eom_seen), a sub- sequent call // to recv() can again read the data of the next message, if the // protocol supports it. msg_eot = (MSG_EOR << 1), // if send() is called with the msg_eot flag set, it places an end // of transmission (EOT) token onto the stream. In half duplex // modes, the EOT signals that no further data will be sent, and // that the other site may try to send data. // Like the EOM, the EOT is mapped as a temporary EOF condition // that can be cleared by setstate(0, eot_seen). Some protocols // also implicitely map EOM to EOT, others allow multiple messages // being sent before an EOT. Protocol drivers have to implement // their `knowledge' about their protocol correctly, i.e. they // have to report the implicite EOT as described above. // NOTE: this protocol does not work for plain TTY and plain File // sockets, since no shutdown-half function is available. However, // normal files -- though all kinds of devices can hide behind a // file -- are unidirectional channels, where at least EOT has no // meaning. TTY devices need a protocol for EOM and EOT to // work. Pipes, UNS and TCP sockets work fine, and Std(IO) sockets // can work, depending on the kinds of channels behind the file // descriptors 0--2. msg_eof = msg_eom, // standard msgf of 4.4 BSD send(2) and recv(2) simply mapped, since // it means the same thing for T/TCP. msg_eor = MSG_EOR, // the msg_eor flag is used with send() in order to have all // buffers flushed on all layers. This is typically used by // a protocol that has placed an EOM token. // standard msgf of BSD send(2) and recv(2), not reliably // implemented msg_oob = MSG_OOB, msg_peek = MSG_PEEK, }; typedef int flags_t; virtual flags_t capabilities() const; virtual flags_t state() const; virtual void setstate(flags_t plus, flags_t minus); /* * Socket methods */ public: virtual bool connect(flags_t mode = send_recv); virtual Socket *accept(flags_t mode = send_recv); virtual size_t send(const char *buf, size_t len, int msgf = 0); virtual size_t recv(char *buf, size_t max, int msgf = 0); virtual void shutdown(flags_t mode = 0); //send_able shuts the recv end virtual void close(flags_t mode = 0); // or silently virtual bool async(bool, pid_t pid = 0); // set asyncrounous mode virtual bool noblock(bool); // set noblock mode // select(2) like methods, however, select is better, since it // provides for selecting multiple file descriptors virtual bool is_sendready(int sec, int usec = 0) const; virtual bool is_recvready(int sec, int usec = 0) const; virtual bool is_exception(int sec, int usec = 0) const; protected: Socket *_lower_level; flags_t _state_plus; flags_t _state_minus; /* * Socket chain organization */ public: static Socket *ctor(const char *address); private: typedef Socket *(*ctor_t)(const char *address, const char *param = NULL, size_t parlen = 0); struct type_entry { const char *name; ctor_t ctor; flags_t cap_plus; flags_t cap_minus; }; static const int n_types; static type_entry types[]; static const type_entry* lookup_type(const char *name, size_t len); private: virtual flags_t cap_plus() const { return 0; } virtual flags_t cap_minus() const { return 0; } virtual Socket *ctor() { return NULL; } // Final Sockets which have no accept() method // should use a copy constructor to copy their private: // disallowed // parameters (like filename for FileSocket), Socket(const Socket &); // because connect will be called for them. const Socket& operator = (const Socket &x); }; #endif