1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include <platform/platform_socket.h>
19 #include <platform/socket.h>
20 
21 #include <event2/util.h>
22 
23 #include <cerrno>
24 #include <gsl/gsl>
25 
26 #ifndef WIN32
27 #include <netdb.h>
28 #include <unistd.h>
29 #endif
30 
31 namespace cb {
32 namespace net {
33 
34 CBSOCKET_PUBLIC_API
closesocket(SOCKET s)35 int closesocket(SOCKET s) {
36 #ifdef WIN32
37     return ::closesocket(s);
38 #else
39     return ::close(s);
40 #endif
41 
42 }
43 
44 CBSOCKET_PUBLIC_API
get_socket_error()45 int get_socket_error() {
46 #ifdef WIN32
47     return WSAGetLastError();
48 #else
49     return errno;
50 #endif
51 }
52 
53 CBSOCKET_PUBLIC_API
bind(SOCKET sock, const struct sockaddr* name, socklen_t namelen)54 int bind(SOCKET sock, const struct sockaddr* name, socklen_t namelen) {
55     return ::bind(sock, name, namelen);
56 }
57 
58 CBSOCKET_PUBLIC_API
listen(SOCKET sock, int backlog)59 int listen(SOCKET sock, int backlog) {
60     return ::listen(sock, backlog);
61 }
62 
63 
64 CBSOCKET_PUBLIC_API
accept(SOCKET sock, struct sockaddr* addr, socklen_t* addrlen)65 SOCKET accept(SOCKET sock, struct sockaddr* addr, socklen_t* addrlen) {
66     return ::accept(sock, addr, addrlen);
67 }
68 
69 CBSOCKET_PUBLIC_API
connect(SOCKET sock, const struct sockaddr* name, size_t namelen)70 int connect(SOCKET sock, const struct sockaddr* name, size_t namelen) {
71 #ifdef WIN32
72     return ::connect(sock, name, gsl::narrow<int>(namelen));
73 #else
74     return ::connect(sock, name, gsl::narrow<socklen_t>(namelen));
75 #endif
76 }
77 
78 CBSOCKET_PUBLIC_API
socket(int domain, int type, int protocol)79 SOCKET socket(int domain, int type, int protocol) {
80     return ::socket(domain, type, protocol);
81 }
82 
83 CBSOCKET_PUBLIC_API
shutdown(SOCKET sock, int how)84 int shutdown(SOCKET sock, int how) {
85     return ::shutdown(sock, how);
86 }
87 
88 CBSOCKET_PUBLIC_API
send(SOCKET sock, const void* buffer, size_t length, int flags)89 ssize_t send(SOCKET sock, const void* buffer, size_t length, int flags) {
90 #ifdef WIN32
91     return ::send(sock, static_cast<const char*>(buffer), gsl::narrow<int>(length), flags);
92 #else
93     return ::send(sock, buffer, length, flags);
94 #endif
95 }
96 
97 CBSOCKET_PUBLIC_API
sendmsg(SOCKET sock, const struct msghdr* message, int flags)98 ssize_t sendmsg(SOCKET sock, const struct msghdr* message, int flags) {
99     return ::sendmsg(sock, message, flags);
100 }
101 
102 CBSOCKET_PUBLIC_API
sendto(SOCKET sock, const void* buffer, size_t length, int flags, const struct sockaddr* dest_addr, socklen_t dest_len)103 ssize_t sendto(SOCKET sock,
104                const void* buffer,
105                size_t length,
106                int flags,
107                const struct sockaddr* dest_addr,
108                socklen_t dest_len) {
109 #ifdef WIN32
110     return ::sendto(sock,
111                     static_cast<const char*>(buffer),
112                     int(length),
113                     flags,
114                     dest_addr,
115                     dest_len);
116 #else
117     return ::sendto(sock, buffer, length, flags, dest_addr, dest_len);
118 #endif
119 }
120 
121 CBSOCKET_PUBLIC_API
recv(SOCKET sock, void* buffer, size_t length, int flags)122 ssize_t recv(SOCKET sock, void* buffer, size_t length, int flags) {
123 #ifdef WIN32
124     return ::recv(sock, static_cast<char*>(buffer), gsl::narrow<int>(length), flags);
125 #else
126     return ::recv(sock, buffer, length, flags);
127 #endif
128 }
129 
130 CBSOCKET_PUBLIC_API
recvfrom(SOCKET sock, void* buffer, size_t length, int flags, struct sockaddr* address, socklen_t* address_len)131 ssize_t recvfrom(SOCKET sock,
132                  void* buffer,
133                  size_t length,
134                  int flags,
135                  struct sockaddr* address,
136                  socklen_t* address_len) {
137 #ifdef WIN32
138     return ::recvfrom(sock,
139                       static_cast<char*>(buffer),
140                       int(length),
141                       flags,
142                       address,
143                       address_len);
144 #else
145     return ::recvfrom(sock, buffer, length, flags, address, address_len);
146 #endif
147 }
148 
149 CBSOCKET_PUBLIC_API
recvmsg(SOCKET sock, struct msghdr* message, int flags)150 ssize_t recvmsg(SOCKET sock, struct msghdr* message, int flags) {
151 #ifdef WIN32
152     int res = 0;
153     for (size_t ii = 0; ii < size_t(message->msg_iovlen); ii++) {
154         auto nr = cb::net::recv(sock,
155                                 message->msg_iov[ii].iov_base,
156                                 message->msg_iov[ii].iov_len,
157                                 0);
158         if (nr == -1) {
159             return (res == 0) ? -1 : res;
160         }
161 
162         res += nr;
163     }
164 
165     return res;
166 #else
167     return ::recvmsg(sock, message, flags);
168 #endif
169 }
170 
171 CBSOCKET_PUBLIC_API
socketpair(int domain, int type, int protocol, SOCKET socket_vector[2])172 int socketpair(int domain, int type, int protocol, SOCKET socket_vector[2]) {
173     return evutil_socketpair(domain,
174                              type,
175                              protocol,
176                              reinterpret_cast<evutil_socket_t*>(socket_vector));
177 }
178 
179 CBSOCKET_PUBLIC_API
getsockopt(SOCKET sock, int level, int option_name, void* option_value, socklen_t* option_len)180 int getsockopt(SOCKET sock,
181                int level,
182                int option_name,
183                void* option_value,
184                socklen_t* option_len) {
185 #ifdef WIN32
186     return ::getsockopt(sock,
187                         level,
188                         option_name,
189                         reinterpret_cast<char*>(option_value),
190                         option_len);
191 #else
192     return ::getsockopt(sock, level, option_name, option_value, option_len);
193 #endif
194 }
195 
196 CBSOCKET_PUBLIC_API
setsockopt(SOCKET sock, int level, int option_name, const void* option_value, socklen_t option_len)197 int setsockopt(SOCKET sock,
198                int level,
199                int option_name,
200                const void* option_value,
201                socklen_t option_len) {
202 #ifdef WIN32
203     return ::setsockopt(sock,
204                         level,
205                         option_name,
206                         reinterpret_cast<const char*>(option_value),
207                         option_len);
208 #else
209     return ::setsockopt(sock, level, option_name, option_value, option_len);
210 #endif
211 }
212 
213 CBSOCKET_PUBLIC_API
set_socket_noblocking(SOCKET sock)214 int set_socket_noblocking(SOCKET sock) {
215     return evutil_make_socket_nonblocking(sock);
216 }
217 
218 CBSOCKET_PUBLIC_API
to_string(const struct sockaddr_storage* addr, socklen_t addr_len)219 std::string to_string(const struct sockaddr_storage* addr, socklen_t addr_len) {
220     char host[50];
221     char port[50];
222 
223     int err = getnameinfo(reinterpret_cast<const struct sockaddr*>(addr),
224                           addr_len,
225                           host,
226                           sizeof(host),
227                           port,
228                           sizeof(port),
229                           NI_NUMERICHOST | NI_NUMERICSERV);
230     if (err != 0) {
231         throw std::runtime_error(
232                 "cb::net::to_string: getnameinfo() failed with error: " +
233                 std::to_string(err));
234     }
235 
236     if (addr->ss_family == AF_INET6) {
237         return "[" + std::string(host) + "]:" + std::string(port);
238     } else {
239         return std::string(host) + ":" + std::string(port);
240     }
241 }
242 
243 CBSOCKET_PUBLIC_API
getsockname(SOCKET sfd)244 std::string getsockname(SOCKET sfd) {
245     sockaddr_storage sock{};
246     socklen_t sock_len = sizeof(sock);
247     if (::getsockname(sfd,
248                       reinterpret_cast<struct sockaddr*>(&sock),
249                       &sock_len) != 0) {
250         throw std::system_error(cb::net::get_socket_error(),
251                                 std::system_category(),
252                                 "getsockname() failed");
253     }
254     return to_string(&sock, sock_len);
255 }
256 
257 CBSOCKET_PUBLIC_API
getpeername(SOCKET sfd)258 std::string getpeername(SOCKET sfd) {
259     sockaddr_storage peer;
260     socklen_t peer_len = sizeof(peer);
261     if (getpeername(sfd,
262                     reinterpret_cast<struct sockaddr*>(&peer),
263                     &peer_len) != 0) {
264         throw std::system_error(cb::net::get_socket_error(),
265                                 std::system_category(),
266                                 "getpeername() failed");
267     }
268     return to_string(&peer, peer_len);
269 }
270 
271 } // namespace net
272 } // namespace cb
273