diff --git a/CMakeLists.txt b/CMakeLists.txt index 69492a43..f7895cb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,8 @@ list ( posix/except.hpp posix/fd.cpp posix/fd.hpp + posix/socket.cpp + posix/socket.hpp ) diff --git a/posix/socket.cpp b/posix/socket.cpp new file mode 100644 index 00000000..24f8d297 --- /dev/null +++ b/posix/socket.cpp @@ -0,0 +1,123 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2017 Danny Robson + */ + +#include "socket.hpp" + +#include "except.hpp" + +#include +#include +#include +#include +#include + +using util::posix::socket; + + +/////////////////////////////////////////////////////////////////////////////// +class lookup { +public: + lookup (const util::view host, int port): + m_addresses (nullptr, freeaddrinfo) + { + struct ::addrinfo hints {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + const struct { + std::string host; + std::string port; + } strings { + .host = std::string (std::begin (host), std::end (host)), + .port = std::to_string (port) + }; + + addrinfo* _addresses; + util::posix::eai::try_code ( + getaddrinfo ( + strings.host.c_str (), + strings.port.c_str (), + &hints, + &_addresses + ) + ); + + m_addresses.reset (_addresses); + }; + + auto begin (void) const { return m_addresses.get (); } + auto end (void) const { return nullptr; } + +private: + std::unique_ptr m_addresses; +}; + + +/////////////////////////////////////////////////////////////////////////////// +static util::posix::fd +connect_host (const util::view host, int port) +{ + for (const auto &info: lookup { host, port }) { + util::posix::fd sock { + socket (info.ai_family, info.ai_socktype, info.ai_protocol) + }; + if (sock == -1) + continue; + + if (connect (sock, info.ai_addr, info.ai_addrlen)) + continue; + + return sock; + } + + throw std::runtime_error ("unable to connect"); +} + + +/////////////////////////////////////////////////////////////////////////////// +socket::socket (int domain, int type): + socket (domain, type, 0) +{ ; } + + +//----------------------------------------------------------------------------- +socket::socket (int domain, int type, int protocol): + fd (::socket (domain, type, protocol)) +{ ; } + + +//----------------------------------------------------------------------------- +socket::socket (util::view host, int port): + fd (connect_host (host, port)) +{ ; } + + +//----------------------------------------------------------------------------- +socket::~socket () +{ ; } + + +/////////////////////////////////////////////////////////////////////////////// +void +socket::connect (util::view host, int port) +{ + for (const auto &info: lookup { host, port }) { + if (!::connect (*this, info.ai_addr, info.ai_addrlen)) + return; + } + + throw std::runtime_error ("unable to reconnect"); +} diff --git a/posix/socket.hpp b/posix/socket.hpp new file mode 100644 index 00000000..ab1ef1c1 --- /dev/null +++ b/posix/socket.hpp @@ -0,0 +1,60 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2017 Danny Robson + */ + +#ifndef CRUFT_UTIL_POSIX_SOCKET_HPP +#define CRUFT_UTIL_POSIX_SOCKET_HPP + +#include "fd.hpp" + +#include "except.hpp" + +#include +#include + + +namespace util::posix { + class socket : public fd { + public: + socket (int domain, int type); + socket (int domain, int type, int protocol); + + socket (util::view host, int port); + + void connect (util::view host, int port); + + // the destructor is provided so that we can operate more cleanly on + // win32 where we must call closesocket instead of the normal close. + // because windows... + ~socket (); + + void shutdown (); + + template + void setoption (int _level, int _name, const ValueT &_value) + { + error::try_code ( + setsockopt ( + native (), + _level, + _name, + &_value, sizeof (_value) + ) + ); + } + }; +}; + +#endif