xref: /6.0.3/platform/cbsocket/socket.cc (revision c6290174)
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/socket.h>
19
20#include <platform/dirutils.h>
21#include <platform/platform.h>
22
23#include <atomic>
24#include <memory>
25#include <string>
26#include <string.h>
27#include <system_error>
28
29#ifndef WIN32
30#include <unistd.h>
31#include <cerrno>
32#include <netdb.h>
33
34#endif
35
36
37struct FileDeleter {
38    void operator()(FILE* fp) { fclose(fp); }
39};
40
41using unique_file_ptr = std::unique_ptr<FILE, FileDeleter>;
42
43
44std::string get_directory(SOCKET socket) {
45    std::string name{"/tmp/"};
46    name.append(std::to_string(cb_getpid()));
47    name.append("-");
48    name.append(std::to_string(socket));
49    return name;
50}
51
52static std::atomic_bool logging;
53
54static inline bool should_log(SOCKET sock) {
55    (void)sock;
56    return logging.load(std::memory_order_relaxed);
57
58#if 0
59    // If you want to do some more filtering (for instance only log traffic
60    // to certain ports you may just modify this piece of code...
61    //
62    // This example will only enable looging where the peer port must be 11210
63    struct sockaddr_storage peer{};
64    socklen_t peer_len = sizeof(peer);
65    int err;
66    if ((err = getpeername(sock,
67                           reinterpret_cast<struct sockaddr*>(&peer),
68                           &peer_len)) != 0) {
69        return false;
70    }
71
72    char host[50];
73    char port[50];
74
75    err = getnameinfo(reinterpret_cast<struct sockaddr*>(&peer),
76                          peer_len,
77                          host, sizeof(host),
78                          port, sizeof(port),
79                          NI_NUMERICHOST | NI_NUMERICSERV);
80    if (err != 0) {
81        return false;
82    }
83
84    return (atoi(port) == 11210);
85#endif
86}
87
88static void log_it(SOCKET sock, const void* data, size_t nb, const char* dir) {
89    std::string fname = get_directory(sock) + dir;
90    unique_file_ptr fp(fopen(fname.c_str(), "ab"));
91    if (!fp) {
92        if (errno == ENOENT) {
93            // The parent directory may not exists (if the socket was
94            // created with lets say any of the libevent methods
95            // (evutil_socketpair for instance).
96            return;
97        }
98        throw std::system_error(errno,
99                                std::system_category(),
100                                "Failed to open socket log file " + fname);
101    }
102
103    auto nw = fwrite(data, 1, nb, fp.get());
104    if (nw != nb) {
105        throw std::system_error(
106            errno,
107            std::system_category(),
108            "Incorrect number of bytes written to socket log file");
109    }
110}
111
112
113namespace cb {
114namespace net {
115
116CBSOCKET_PUBLIC_API
117void set_socket_logging(bool enable) {
118    logging.store(enable);
119}
120
121CBSOCKET_PUBLIC_API
122int closesocket(SOCKET s) {
123    int ret;
124#ifdef WIN32
125    ret = ::closesocket(s);
126#else
127    ret = ::close(s);
128#endif
129
130    if (ret == 0 && logging) {
131        try {
132            cb::io::rmrf(get_directory(s));
133        } catch (...) {
134        }
135    }
136
137    return ret;
138}
139
140CBSOCKET_PUBLIC_API
141int get_socket_error() {
142#ifdef WIN32
143    return WSAGetLastError();
144#else
145    return errno;
146#endif
147}
148
149CBSOCKET_PUBLIC_API
150int bind(SOCKET sock, const struct sockaddr* name, socklen_t namelen) {
151    return ::bind(sock, name, namelen);
152}
153
154CBSOCKET_PUBLIC_API
155SOCKET accept(SOCKET sock, struct sockaddr* addr, socklen_t* addrlen) {
156    auto ret = ::accept(sock, addr, addrlen);
157    if (ret != INVALID_SOCKET && logging) {
158        cb::io::mkdirp(get_directory(ret));
159    }
160
161    return ret;
162}
163
164CBSOCKET_PUBLIC_API
165int connect(SOCKET sock, const struct sockaddr* name, int namelen) {
166    return ::connect(sock, name, namelen);
167}
168
169CBSOCKET_PUBLIC_API
170SOCKET socket(int domain, int type, int protocol) {
171    auto ret = ::socket(domain, type, protocol);
172    if (ret != INVALID_SOCKET && logging) {
173        cb::io::mkdirp(get_directory(ret));
174    }
175    return ret;
176}
177
178CBSOCKET_PUBLIC_API
179int shutdown(SOCKET sock, int how) {
180    return ::shutdown(sock, how);
181}
182
183CBSOCKET_PUBLIC_API
184ssize_t send(SOCKET sock, const void* buffer, size_t length, int flags) {
185#ifdef WIN32
186    auto ret =
187            ::send(sock, static_cast<const char*>(buffer), int(length), flags);
188#else
189    auto ret = ::send(sock, buffer, length, flags);
190#endif
191    if (ret > 0 && should_log(sock)) {
192        log_it(sock, buffer, size_t(ret), "/w");
193    }
194
195    return ret;
196}
197
198CBSOCKET_PUBLIC_API
199ssize_t sendmsg(SOCKET sock, const struct msghdr* message, int flags) {
200    if (logging) {
201        int res = 0;
202        for (size_t ii = 0; ii < size_t(message->msg_iovlen); ii++) {
203            auto nw = cb::net::send(sock,
204                                    message->msg_iov[ii].iov_base,
205                                    message->msg_iov[ii].iov_len,
206                                    0);
207            if (nw == -1) {
208                return (res == 0) ? -1 : res;
209            }
210
211            res += nw;
212        }
213
214        return res;
215    }
216
217    return ::sendmsg(sock, message, flags);
218}
219
220CBSOCKET_PUBLIC_API
221ssize_t sendto(SOCKET sock,
222               const void* buffer,
223               size_t length,
224               int flags,
225               const struct sockaddr* dest_addr,
226               socklen_t dest_len) {
227    if (logging) {
228        throw std::runtime_error("cb::net::sendto: not implemented");
229    }
230
231#ifdef WIN32
232    return ::sendto(sock,
233                    static_cast<const char*>(buffer),
234                    int(length),
235                    flags,
236                    dest_addr,
237                    dest_len);
238#else
239    return ::sendto(sock, buffer, length, flags, dest_addr, dest_len);
240#endif
241}
242
243CBSOCKET_PUBLIC_API
244ssize_t recv(SOCKET sock, void* buffer, size_t length, int flags) {
245#ifdef WIN32
246    auto ret = ::recv(sock, static_cast<char*>(buffer), length, flags);
247#else
248    auto ret = ::recv(sock, buffer, length, flags);
249#endif
250
251    if (ret > 0 && should_log(sock)) {
252        log_it(sock, buffer, size_t(ret), "/r");
253    }
254
255    return ret;
256}
257
258CBSOCKET_PUBLIC_API
259ssize_t recvfrom(SOCKET sock,
260                 void* buffer,
261                 size_t length,
262                 int flags,
263                 struct sockaddr* address,
264                 socklen_t* address_len) {
265    if (logging) {
266        throw std::runtime_error("cb::net::recvfrom: not implemented");
267    }
268
269#ifdef WIN32
270    return ::recvfrom(sock,
271                      static_cast<char*>(buffer),
272                      int(length),
273                      flags,
274                      address,
275                      address_len);
276#else
277    return ::recvfrom(sock, buffer, length, flags, address, address_len);
278#endif
279}
280
281CBSOCKET_PUBLIC_API
282ssize_t recvmsg(SOCKET sock, struct msghdr* message, int flags) {
283    if (logging) {
284        int res = 0;
285        for (size_t ii = 0; ii < size_t(message->msg_iovlen); ii++) {
286            auto nr = cb::net::recv(sock,
287                                    message->msg_iov[ii].iov_base,
288                                    message->msg_iov[ii].iov_len,
289                                    0);
290            if (nr == -1) {
291                return (res == 0) ? -1 : res;
292            }
293
294            res += nr;
295        }
296
297        return res;
298    }
299
300#ifdef WIN32
301    throw std::runtime_error("cb::net::recvmsg: Not implemented for win32");
302#else
303    return ::recvmsg(sock, message, flags);
304#endif
305}
306
307} // namespace net
308} // namespace cb
309
310// C API which wraps into the C++ API
311
312CBSOCKET_PUBLIC_API
313int cb_closesocket(SOCKET s) {
314    return cb::net::closesocket(s);
315}
316
317CBSOCKET_PUBLIC_API
318int cb_get_socket_error() {
319    return cb::net::get_socket_error();
320}
321
322CBSOCKET_PUBLIC_API
323int cb_bind(SOCKET sock, const struct sockaddr* name, socklen_t namelen) {
324    return cb::net::bind(sock, name, namelen);
325}
326
327CBSOCKET_PUBLIC_API
328SOCKET cb_accept(SOCKET sock, struct sockaddr* addr, socklen_t* addrlen) {
329    return cb::net::accept(sock, addr, addrlen);
330}
331
332CBSOCKET_PUBLIC_API
333int cb_connect(SOCKET sock, const struct sockaddr* name, int namelen) {
334    return cb::net::connect(sock, name, namelen);
335}
336
337CBSOCKET_PUBLIC_API
338SOCKET cb_socket(int domain, int type, int protocol) {
339    return cb::net::socket(domain, type, protocol);
340}
341
342CBSOCKET_PUBLIC_API
343int cb_shutdown(SOCKET sock, int how) {
344    return cb::net::shutdown(sock, how);
345}
346
347CBSOCKET_PUBLIC_API
348ssize_t cb_send(SOCKET sock, const void* buffer, size_t length, int flags) {
349    return cb::net::send(sock, buffer, length, flags);
350}
351
352CBSOCKET_PUBLIC_API
353ssize_t cb_sendmsg(SOCKET sock, const struct msghdr* message, int flags) {
354    return cb::net::sendmsg(sock, message, flags);
355}
356
357CBSOCKET_PUBLIC_API
358ssize_t cb_sendto(SOCKET sock,
359                  const void* buffer,
360                  size_t length,
361                  int flags,
362                  const struct sockaddr* dest_addr,
363                  socklen_t dest_len) {
364    return cb::net::sendto(sock, buffer, length, flags, dest_addr, dest_len);
365}
366
367CBSOCKET_PUBLIC_API
368ssize_t cb_recv(SOCKET sock, void* buffer, size_t length, int flags) {
369    return cb::net::recv(sock, buffer, length, flags);
370}
371
372CBSOCKET_PUBLIC_API
373ssize_t cb_recvfrom(SOCKET sock,
374                    void* buffer,
375                    size_t length,
376                    int flags,
377                    struct sockaddr* address,
378                    socklen_t* address_len) {
379    return cb::net::recvfrom(sock, buffer, length, flags, address, address_len);
380}
381
382CBSOCKET_PUBLIC_API
383ssize_t cb_recvmsg(SOCKET sock, struct msghdr* message, int flags) {
384    return cb::net::recvmsg(sock, message, flags);
385}
386
387CBSOCKET_PUBLIC_API
388void cb_set_socket_logging(bool enable) {
389    cb::net::set_socket_logging(enable);
390}
391