/** * 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. * * net/socket.h * (c) 2005-2008 Murat Deligonul * * Encapsulates a TCP stream socket, supports IPv6 & SSL */ #ifndef __NET_SOCKET_H # define __NET_SOCKET_H #include #include #include #include #include #include #include #include #ifdef HAVE_SSL # define OPENSSL_NO_KRB5 /* prevent compilation error on RH Linux 9, et al */ # include # include # include #endif #include "util/exception.h" #include "util/strings.h" #include "io/engine.h" #include "io/pollable.h" #include "io/buffer.h" #include "net/types.h" #include "net/resolver.h" #include "net/radaptor.h" #include "debug.h" namespace net { struct socket_exception_tag { }; typedef util::exception socket_exception; class socket : public io::pollable { private: /** Common to all sockets **/ static class io::engine * engine; static class resolver * resolver; static class radaptor * radaptor; #ifdef HAVE_SSL static SSL_CTX *ssl_ctx; static char *tls_rand_file; static int seed_PRNG(); SSL *ssl; #endif public: static const size_t BUFFER_MINIMUM = io::buffer::MINIMUM; static const size_t BUFFER_MAXIMUM = io::buffer::MAXIMUM; /** Static member functions **/ static void assign_resolver(class resolver *r) { socket::resolver = r; } static void assign_engine(class io::engine * e) { delete radaptor; socket::engine = e; socket::radaptor = new class radaptor(*resolver, *engine); } static int set_nonblocking(int f) { return fcntl(f, F_SETFL, fcntl(f, F_GETFL,0) | O_NONBLOCK); } static class resolver * get_resolver() { return socket::resolver; } static void compress() { engine->compress(); } /** Socket state **/ enum state { NEW = 0, ACCEPTING, OPEN, CONNECT_RESOLVING, CONNECTING, CONNECTED, LISTEN_RESOLVING, LISTENING, CLOSED }; enum sock_option { SOCK_DEFAULT = 0, SOCK_SSL = 2 }; private: /** resolver_callback functions **/ int async_lookup_finished(const resolver::result *); int async_lookup_failed(const resolver::result *); private: /** basic properties **/ int family; enum state state; int options; int lookup_id; /* id number of current async. name resolution */ /** resolver callback wrapper **/ const net::resolver_callback_wrapper r_callback; /** I/O buffers **/ io::buffer ibuff; io::buffer obuff; /** address information **/ ap_pair local, peer; /** resolver helper data **/ resolver::request * tmp_req; std::pair * interface_data; public: /** * Create a socket of protocol with socket() */ socket(int family, int options, size_t, size_t); /** * accept() a connection from another socket` */ socket(socket * source, size_t, size_t); virtual ~socket() { close(); } /** re-create a socket **/ int open(int, int = IPPROTO_TCP); /** accessors **/ int get_state() const { return state; } int get_family() const { return family; } int get_options() const { return options; } /** peer/local address & port information **/ const ap_pair& get_peer() const { return peer; } const ap_pair& get_local() const { return local; } const char * peer_addr() const { return peer.first.c_str(); } const char * local_addr() const { return local.first.c_str(); } unsigned short peer_port() const { return peer.second; } unsigned short local_port() const { return local.second; } /** buffer information **/ io::buffer& get_ibuff() { return ibuff; } io::buffer& get_obuff() { return obuff; } size_t ibuff_size() const { return ibuff.size(); } size_t obuff_size() const { return obuff.size(); } /** make it do stuff **/ int async_connect(const char *, const char *, unsigned short, int = SOCK_DEFAULT); int listen(const char *, unsigned short, int, int = SOCK_DEFAULT); int listen(const char *, const char *, int, int = SOCK_DEFAULT); int close(); private: /** 'pollable' interface **/ virtual int event_callback(int); int update_addr_info(bool); int update_addr_info(bool, const struct sockaddr *, size_t); int check_for_close() const; static int recv_test(int); public: /** * Event handlers: * NOTE: Any handler that causes the closing of the socket MUST return < 0. * This is to halt processing of additional events and avoid invoking additional callbacks * on potentially deleted objects. * The error related callbacks (disconnects, connect fail, error) are always assumed to have * closed the socket. */ virtual int on_readable() = 0; virtual int on_writeable() = 0; virtual void on_disconnect(int); virtual void on_connect(); virtual void on_connect_fail(int); virtual void on_connecting(); /** * SSL functions... */ #ifdef HAVE_SSL private: int switch_to_ssl(); int accept_to_ssl(); public: static int init_ssl(const char *); static int shutdown_ssl(); #endif void set_events(int events) { if (get_fd() > -1) { engine->set_events(this, events); } } int events() const { return get_events(); } /** I/O functions ** *** (buffered) reads and writes *** int read(int = 0); int queue(const void *, int); int write(const void * , int); *** Flush input buffer ... *** int flush(socket *); ** ... to another socket (output buff) ** int flush(int, size_t); ** ... to a raw file descriptor ** *** Flush output buffer ... *** int flushO(socket *); ** ... to another socket (input buff) ** int flushO(size_t ); ** ... out our own fd *** To clear buffers *** void clear(); void clearO(); **/ int printf(const char *, ...); int printfQ(const char *, ...); int printf_raw(const char *, va_list); /* * read() from file descriptor & add to buffer * return: > 0 = success (bytes added) * 0 = nothing to add * < 0 = error condition * [... error codes same as io::buffer error codes ...] */ int read(size_t len = 0) { #ifdef HAVE_SSL if (ssl) { return ibuff.insert(ssl, len); } #endif return ibuff.insert(get_fd(), len); } /** * Buffer size bytes, and attempt to send as much as possible * return # bytes sent */ int write(const void * data , size_t size) { int r = queue(data, size); if (r < 0) { return r; } return obuff.flush(get_fd()); } /** * Buffer 'size' bytes, but do not send. * Return # bytes queued. */ int queue(const void * data, size_t size) { return obuff.insert(data, size); } /** * Flush input buffer to output buffer of socket. */ int flush(socket * target) { return ibuff.flush(&target->obuff); } /** * Flush input buffer to a file descriptor. */ int flush(int fd, size_t size) { return ibuff.flush(fd, size); } /** * Flush input buffer to a ostream. */ int flush(std::ostream& out, size_t size) { return ibuff.flush(out, size); } /** * Flush output buffer to input buffer of another socket */ int flushO(socket * target) { return obuff.flush(&target->ibuff); } /** * Flush output buffer out our own fd. */ int flushO(size_t size) { #ifdef HAVE_SSL if (ssl) { return obuff.flush(ssl, size); } #endif return obuff.flush(get_fd(), size); } int flushO() { return flushO(obuff_size()); } /* determine input/output buffer capacity */ size_t buffer_available() { return ibuff.available(); } size_t buffer_availableO() { return obuff.available(); } /** * Clear buffers. */ void clear() { ibuff.clear(); } void clearO() { obuff.clear(); } void optimize_buffers() { obuff.optimize(); ibuff.optimize(); } private: // non-copyable socket(const socket &); socket & operator=(socket &); }; } /* namespace net */ #endif /* __NET_SOCKET_H */