socket.h 15.4 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
#pragma once 

#include "socket-cpp/sock_address.h"
#include <chrono>
#include <string>
#include <tuple>

namespace osdev {
namespace components {
namespace socket-cpp {

#if !defined(SOCKPP_SOCKET_T_DEFINED)
	typedef int socket_t;				///< The OS socket handle
	const socket_t INVALID_SOCKET = -1;	///< Invalid socket descriptor
	#define SOCKPP_SOCKET_T_DEFINED
#endif

/**
 * Converts a number of microseconds to a relative timeval.
 * @param dur A chrono duration of microseconds.
 * @return A timeval
 */
timeval to_timeval(const std::chrono::microseconds& dur);

/**
 * Converts a chrono duration to a relative timeval.
 * @param dur A chrono duration.
 * @return A timeval.
 */
template<class Rep, class Period>
timeval to_timeval(const std::chrono::duration<Rep,Period>& dur) {
	return to_timeval(std::chrono::duration_cast<std::chrono::microseconds>(dur));
}

/**
 * Converts a relative timeval to a chrono duration.
 * @param tv A timeval.
 * @return A chrono duration.
 */
inline std::chrono::microseconds to_duration(const timeval& tv)
{
    auto dur = std::chrono::seconds{tv.tv_sec}
				+ std::chrono::microseconds{tv.tv_usec};
    return std::chrono::duration_cast<std::chrono::microseconds>(dur);
}

/**
 * Converts an absolute timeval to a chrono time_point.
 * @param tv A timeval.
 * @return A chrono time_point.
 */
inline std::chrono::system_clock::time_point to_timepoint(const timeval& tv)
{
	return std::chrono::system_clock::time_point {
        std::chrono::duration_cast<std::chrono::system_clock::duration>(to_duration(tv))
	};
}

/////////////////////////////////////////////////////////////////////////////

/**
 * Base class for socket objects.
 *
 * This class wraps an OS socket handle and maintains strict ownership
 * semantics. If a socket object has a valid, open socket, then it owns the
 * handle and will close it when the object is destroyed.
 *
 * Objects of this class are not copyable, but they are moveable.
 */
class socket
{
	/** The OS integer socket handle */
	socket_t handle_;
	/** Cache of the last error (errno) */
	mutable int lastErr_;
	/**
	 * The OS-specific function to close a socket handle/
	 * @param h The OS socket handle.
	 * @return @em true if the sock is closed, @em false on error.
	 */
	bool close(socket_t h);

	// Non-copyable.
	socket(const socket&) =delete;
	socket& operator=(const socket&) =delete;

protected:
	/**
	 * Closes the socket without checking for errors or updating the last
	 * error.
	 * This is used in internal open/connect type functions that fail after
	 * creating the socket, but want to preserve the previous failure
	 * condition.
	 * Assumes that the socket handle is valid.
	 * @return Always returns @em false.
	 */
	bool close_on_err() {
		close(release());
		return false;
	}
	/**
	 * OS-specific means to retrieve the last error from an operation.
	 * This should be called after a failed system call to set the
	 * lastErr_ member variable. Normally this would be called from
	 * @ref check_ret.
	 */
	static int get_last_error();
	/**
	 * Cache the last system error code into this object.
	 * This should be called after a failed system call to store the error
	 * value.
	 */
	void set_last_error() {
		lastErr_ = get_last_error();
	}
	/**
	 * Checks the value and if less than zero, sets last error.
     * @tparam T A signed integer type of any size
	 * @param ret The return value from a library or system call.
	 * @return Returns the value sent to it, `ret`.
	 */
    template <typename T>
	T check_ret(T ret) const{
		lastErr_ = (ret < 0) ? get_last_error() : 0;
		return ret;
	}
	/**
     * Checks the value and if less than zero, sets last error. 
     * @tparam T A signed integer type of any size
	 * @param ret The return value from a library or system call.
	 * @return @em true if the value is a typical system success value (>=0)
	 *  	   or @em false is is an error (<0)
	 */
    template <typename T>
	bool check_ret_bool(T ret) const{
		lastErr_ = (ret < 0) ? get_last_error() : 0;
		return ret >= 0;
	}
	/**
	 * Checks the value and if it is not a valid socket, sets last error.
	 * This is specifically required for Windows which uses an unsigned type
	 * for its SOCKET.
	 * @param ret The return value from a library or system call that returns
	 *  		  a socket, such as socket() or accept().
	 * @return Returns the value sent to it, `ret`.
	 */
	socket_t check_socket(socket_t ret) const {
		lastErr_ = (ret == INVALID_SOCKET) ? get_last_error() : 0;
		return ret;
	}
    /**
     * Checks the value and if it is INVALID_SOCKET, sets last error. 
	 * This is specifically required for Windows which uses an unsigned type
	 * for its SOCKET.
	 * @param ret The return value from a library or system call that returns
	 *  		  a socket such as socket() or accept().
	 * @return @em true if the value is a valid socket (not INVALID_SOCKET)
	 *  	   or @em false is is an error (INVALID_SOCKET)
	 */
	bool check_socket_bool(socket_t ret) const{
		lastErr_ = (ret == INVALID_SOCKET) ? get_last_error() : 0;
		return ret != INVALID_SOCKET;
	}

public:
	/**
	 * Creates an unconnected (invalid) socket
	 */
	socket() : handle_(INVALID_SOCKET), lastErr_(0) {}
	/**
	 * Creates a socket from an existing OS socket handle.
	 * The object takes ownership of the handle and will close it when
	 * destroyed.
	 * @param h An OS socket handle.
	 */
	explicit socket(socket_t h) : handle_(h), lastErr_(0) {}
	/**
	 * Move constructor.
	 * This takes ownership of the underlying handle in sock.
	 * @param sock An rvalue reference to a socket object.
	 */
	socket(socket&& sock) noexcept
			: handle_(sock.handle_), lastErr_(sock.lastErr_) {
		sock.handle_ = INVALID_SOCKET;
	}
	/**
	 * Destructor closes the socket.
	 */
	virtual ~socket() { close(); }
	/**
	 * Initializes the socket (sockpp) library.
	 * This is only required for Win32. On platforms that use a standard
	 * socket implementation this is an empty call.
	 */
	static void initialize();
	/**
	 * Shuts down the socket library.
	 * This is only required for Win32. On platforms that use a standard
	 * socket implementation this is an empty call.
	 */
	static void destroy();
	/**
	 * Creates a socket with the specified communications characterics.
	 * Not that this is not normally how a socket is creates in the sockpp
	 * library. Applications would typically create a connector (client) or
	 * acceptor (server) socket which would take care of the details.
	 *
	 * This is included for completeness or for creating different types of
	 * sockets than are supported by the library.
	 *
	 * @param domain The communications domain for the sockets (i.e. the
	 *  			 address family)
	 * @param type The communication semantics for the sockets (SOCK_STREAM,
	 *  		   SOCK_DGRAM, etc).
	 * @param protocol The particular protocol to be used with the sockets
	 *
	 * @return A socket with the requested communications characteristics.
	 */
	static socket create(int domain, int type, int protocol=0);
	/**
	 * Determines if the socket is open (valid).
	 * @return @em true if the socket is open, @em false otherwise.
	 */
	bool is_open() const { return handle_ != INVALID_SOCKET; }
	/**
	 * Determines if the socket is closed or in an error state.
	 * @return @em true if the socket is closed or in an error state, @em
	 *  	   false otherwise.
	 */
	bool operator!() const {
		return handle_ == INVALID_SOCKET || lastErr_ != 0;
	}
	/**
	 * Determines if the socket is open and in an error-free state.
	 * @return @em true if the socket is open and in an error-free state,
	 *  	   @em false otherwise.
	 */
	explicit operator bool() const {
		return handle_ != INVALID_SOCKET && lastErr_ == 0;
	}
	/**
	 * Get the underlying OS socket handle.
	 * @return The underlying OS socket handle.
	 */
	socket_t handle() const { return handle_; }
	/**
	 * Gets the network family of the address to which the socket is bound.
	 * @return The network family of the address (AF_INET, etc) to which the
	 *  	   socket is bound. If the socket is not bound, or the address
	 *  	   is not known, returns AF_UNSPEC.
	 */
	virtual sa_family_t family() const {
		return address().family();
	}
	/**
	 * Creates a new socket that refers to this one.
	 * This creates a new object with an independent lifetime, but refers
	 * back to this same socket. On most systems, this duplicates the file
	 * handle using the dup() call.
	 * A typical use of this is to have separate threads for reading and
	 * writing the socket. One thread would get the original socket and the
	 * other would get the cloned one.
	 * @return A new socket object that refers to the same socket as this
	 *  	   one.
	 */
	socket clone() const;
	/**
	 * Creates a pair of connected sockets.
	 *
	 * Whether this will work at all is highly system and domain dependent.
	 * Currently it is only known to work for Unix-domain sockets on *nix
	 * systems.
	 *
	 * Note that applications would normally call this from a derived socket
	 * class which would return the properly type-cast sockets to match the
	 * `domain` and `type`.
	 *
	 * @param domain The communications domain for the sockets (i.e. the
	 *  			 address family)
	 * @param type The communication semantics for the sockets (SOCK_STREAM,
	 *  		   SOCK_DGRAM, etc).
	 * @param protocol The particular protocol to be used with the sockets
	 *
	 * @return A std::tuple of sockets. On error both sockets will be in an
	 *  	   error state with the
	 */
	static std::tuple<socket, socket> pair(int domain, int type, int protocol=0);
	/**
	 * Clears the error flag for the object.
	 * @param val The value to set the flag, normally zero.
	 */
	void clear(int val=0) { lastErr_ = val; }
	/**
	 * Releases ownership of the underlying socket object.
	 * @return The OS socket handle.
	 */
	socket_t release() {
		socket_t h = handle_;
		handle_ = INVALID_SOCKET;
		return h;
	}
	/**
	 * Replaces the underlying managed socket object.
	 * @param h The new socket handle to manage.
	 */
	void reset(socket_t h=INVALID_SOCKET);
	/**
	 * Move assignment.
	 * This assigns ownership of the socket from the other object to this
	 * one.
	 * @return A reference to this object.
	 */
	socket& operator=(socket&& sock) noexcept {
		// Give our handle to the other to close.
		std::swap(handle_, sock.handle_);
		lastErr_ = sock.lastErr_;
		return *this;
	}
	/**
	 * Binds the socket to the specified address.
	 * @param addr The address to which we get bound.
	 * @return @em true on success, @em false on error
	 */
	bool bind(const sock_address& addr);
	/**
	 * Gets the local address to which the socket is bound.
	 * @return The local address to which the socket is bound.
	 */
	sock_address_any address() const;
	/**
	 * Gets the address of the remote peer, if this socket is connected.
	 * @return The address of the remote peer, if this socket is connected.
	 */
	sock_address_any peer_address() const;
    /**
     * Gets the value of a socket option.
     *
     * This is a thin wrapper for the system `getsockopt`.
     *
     * @param level The protocol level at which the option resides, such as
     *              SOL_SOCKET.
     * @param optname The option passed directly to the protocol module.
     * @param optval The buffer for the value to retrieve
     * @param optlen Initially contains the lenth of the buffer, and on return
     *               contains the length of the value retrieved.
     *
     * @return bool @em true if the value was retrieved, @em false if an error
     *         occurred.
     */
    bool get_option(int level, int optname, void* optval, socklen_t* optlen) const;
    /**
     * Gets the value of a socket option.
     *
     * @param level The protocol level at which the option resides, such as
     *              SOL_SOCKET.
     * @param optname The option passed directly to the protocol module.
     * @param val The value to retrieve
     * @return bool @em true if the value was retrieved, @em false if an error
     *         occurred.
     */
	template <typename T>
    bool get_option(int level, int optname, T* val) const {
		socklen_t len = sizeof(T);
		return get_option(level, optname, (void*) val, &len);
	}
    /**
     * Sets the value of a socket option.
     *
     * This is a thin wrapper for the system `setsockopt`.
     *
     * @param level The protocol level at which the option resides, such as
     *              SOL_SOCKET.
     * @param optname The option passed directly to the protocol module.
     * @param optval The buffer with the value to set.
     * @param optlen Contains the lenth of the value buffer.
     *
     * @return bool @em true if the value was set, @em false if an error
     *         occurred.
     */
    bool set_option(int level, int optname, const void* optval, socklen_t optlen);
    /**
     * Sets the value of a socket option.
     *
     * @param level The protocol level at which the option resides, such as
     *              SOL_SOCKET.
     * @param optname The option passed directly to the protocol module.
     * @param val The value to set.
     *
     * @return bool @em true if the value was set, @em false if an error
     *         occurred.
     */
	template <typename T>
    bool set_option(int level, int optname, const T& val) {
		return set_option(level, optname, (void*) &val, sizeof(T));
	}
	/**
	 * Places the socket into or out of non-blocking mode.
	 * When in non-blocking mode, a call that is not immediately ready to
	 * complete (read, write, accept, etc) will return immediately with the
	 * error EWOULDBLOCK.
	 * @param on Whether to turn non-blocking mode on or off.
	 * @return @em true on success, @em false on failure.
	 */
	bool set_non_blocking(bool on=true);
    /**
     * Gets a string describing the specified error.
     * This is typically the returned message from the system strerror().
     * @param errNum The error number.
     * @return A string describing the specified error.
     */
    static std::string error_str(int errNum);
	/**
	 * Gets the code for the last errror.
	 * This is typically the code from the underlying OS operation.
	 * @return The code for the last errror.
	 */
	int last_error() const { return lastErr_; }
	/**
	 * Gets a string describing the last errror.
	 * This is typically the returned message from the system strerror().
	 * @return A string describing the last errror.
	 */
	std::string last_error_str() const {
        return error_str(lastErr_);
    }
	/**
	 * Shuts down all or part of the full-duplex connection.
	 * @param how Which part of the connection should be shut:
	 *  	@li SHUT_RD   (0) Further reads disallowed.
	 *  	@li SHUT_WR   (1) Further writes disallowed
	 *  	@li SHUT_RDWR (2) Further reads and writes disallowed.
	 * @return @em true on success, @em false on error.
	 */
	bool shutdown(int how=SHUT_RDWR);
	/**
	 * Closes the socket.
	 * After closing the socket, the handle is @em invalid, and can not be
	 * used again until reassigned.
	 * @return @em true if the sock is closed, @em false on error.
	 */
	bool close();
};

/**
 * RAII class to initialize and then shut down the library.
 * A single object of this class can be declared before any other classes in
 * the library are used. The lifetime of the object should span the use of
 * the other classes in the library, so declaring an object at the top of
 * main() is usually the best choice.
 * This is only required on some platforms, particularly Windows, but is
 * harmless on other platforms. On some, such as POSIX, the initializer sets
 * optional parameters for the library, and the destructor does nothing.
 */
class socket_initializer
{
public:
	socket_initializer() { socket::initialize(); }
	~socket_initializer() { socket::destroy(); }
};

}   // End namespace socket-cpp
}   // End namespace components
}   // End namespace osdev