xref: /6.0.3/platform/cbsocket/socket.cc (revision c29955ea)
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 <event2/util.h>
24
25#include <atomic>
26#include <cerrno>
27#include <memory>
28#include <mutex>
29#include <string>
30#include <string.h>
31#include <system_error>
32#include <unordered_map>
33
34#ifndef WIN32
35#include <unistd.h>
36#include <netdb.h>
37#endif
38
39/**
40 * We cache each directory name in an unordered map where we remove
41 * the entry every time the socket is successfully closed. By
42 * doing this we can create a new file every time the same socket
43 * is reopened.
44 */
45std::mutex mutex;
46std::unordered_map<SOCKET, std::string> name_map;
47
48std::string get_directory(SOCKET socket) {
49    std::lock_guard<std::mutex> guard(mutex);
50
51    auto iter = name_map.find(socket);
52    if (iter == name_map.end()) {
53        std::string prefix{"/tmp/"};
54        prefix.append(std::to_string(cb_getpid()));
55        prefix.append("-");
56        prefix.append(std::to_string(socket));
57        prefix.append("-");
58
59        std::string name;
60        uint32_t seqno = 0;
61        do {
62            name = prefix+ std::to_string(seqno++);
63        } while (cb::io::isDirectory(name));
64
65        name_map[socket] = name;
66        return name;
67    }
68
69    return iter->second;
70}
71
72
73static void log_it(SOCKET sock, const void* data, size_t nb, const char* dir) {
74    struct FileDeleter {
75        void operator()(FILE* fp) { fclose(fp); }
76    };
77
78    std::string fname = get_directory(sock) + dir;
79    std::unique_ptr<FILE, FileDeleter> fp(fopen(fname.c_str(), "ab"));
80
81    if (!fp) {
82        if (errno == ENOENT) {
83            // The parent directory may not exists (if the socket was
84            // created with lets say any of the libevent methods
85            // (evutil_socketpair for instance).
86            return;
87        }
88        throw std::system_error(errno,
89                                std::system_category(),
90                                "Failed to open socket log file " + fname);
91    }
92
93    auto nw = fwrite(data, 1, nb, fp.get());
94    if (nw != nb) {
95        throw std::system_error(
96            errno,
97            std::system_category(),
98            "Incorrect number of bytes written to socket log file");
99    }
100}
101
102static bool defaultFilterHandler(SOCKET sock) {
103    (void)sock;
104    return true;
105}
106
107static void defaultCloseHandler(SOCKET sock, const std::string& dir) {
108    try {
109        cb::io::rmrf(dir);
110    } catch (...) {
111        // Empty
112    }
113}
114
115static std::atomic<bool(*)(SOCKET)> filter_handler {defaultFilterHandler};
116static std::atomic<void(*)(SOCKET, const std::string&)> close_handler {defaultCloseHandler};
117static std::atomic_bool logging;
118
119static inline bool should_log(SOCKET sock) {
120    if (!logging.load(std::memory_order_relaxed)) {
121        return false;
122    }
123
124    // Logging is enabled.. check the filter for this socket
125    return filter_handler.load()(sock);
126}
127
128namespace cb {
129namespace net {
130
131CBSOCKET_PUBLIC_API
132void set_log_filter_handler(bool (* handler)(SOCKET)) {
133    if (handler == nullptr) {
134        filter_handler.store(defaultFilterHandler);
135    } else {
136        filter_handler.store(handler);
137    }
138}
139
140CBSOCKET_PUBLIC_API
141void set_on_close_handler(void (* handler)(SOCKET, const std::string& dir)) {
142    if (handler == nullptr) {
143        close_handler.store(defaultCloseHandler);
144    } else {
145        close_handler.store(handler);
146    }
147}
148
149CBSOCKET_PUBLIC_API
150void set_socket_logging(bool enable) {
151    logging.store(enable);
152}
153
154CBSOCKET_PUBLIC_API
155int closesocket(SOCKET s) {
156    if (logging) {
157        std::lock_guard<std::mutex> guard(mutex);
158        int ret;
159#ifdef WIN32
160        ret = ::closesocket(s);
161#else
162        ret = ::close(s);
163#endif
164
165        if (ret == 0 && logging) {
166            std::string name;
167            auto iter = name_map.find(s);
168            if (iter != name_map.end()) {
169                name = iter->second;
170                name_map.erase(iter);
171                close_handler.load()(s, name);
172            }
173        }
174
175        return ret;
176    }
177
178#ifdef WIN32
179    return ::closesocket(s);
180#else
181    return ::close(s);
182#endif
183
184}
185
186CBSOCKET_PUBLIC_API
187int get_socket_error() {
188#ifdef WIN32
189    return WSAGetLastError();
190#else
191    return errno;
192#endif
193}
194
195CBSOCKET_PUBLIC_API
196int bind(SOCKET sock, const struct sockaddr* name, socklen_t namelen) {
197    return ::bind(sock, name, namelen);
198}
199
200CBSOCKET_PUBLIC_API
201SOCKET accept(SOCKET sock, struct sockaddr* addr, socklen_t* addrlen) {
202    auto ret = ::accept(sock, addr, addrlen);
203    if (ret != INVALID_SOCKET && should_log(ret)) {
204        cb::io::mkdirp(get_directory(ret));
205    }
206
207    return ret;
208}
209
210CBSOCKET_PUBLIC_API
211int connect(SOCKET sock, const struct sockaddr* name, int namelen) {
212    return ::connect(sock, name, namelen);
213}
214
215CBSOCKET_PUBLIC_API
216SOCKET socket(int domain, int type, int protocol) {
217    auto ret = ::socket(domain, type, protocol);
218    if (ret != INVALID_SOCKET && should_log(ret)) {
219        cb::io::mkdirp(get_directory(ret));
220    }
221    return ret;
222}
223
224CBSOCKET_PUBLIC_API
225int shutdown(SOCKET sock, int how) {
226    return ::shutdown(sock, how);
227}
228
229CBSOCKET_PUBLIC_API
230ssize_t send(SOCKET sock, const void* buffer, size_t length, int flags) {
231#ifdef WIN32
232    auto ret =
233            ::send(sock, static_cast<const char*>(buffer), int(length), flags);
234#else
235    auto ret = ::send(sock, buffer, length, flags);
236#endif
237    if (ret > 0 && should_log(sock)) {
238        log_it(sock, buffer, size_t(ret), "/w");
239    }
240
241    return ret;
242}
243
244CBSOCKET_PUBLIC_API
245ssize_t sendmsg(SOCKET sock, const struct msghdr* message, int flags) {
246    if (logging) {
247        int res = 0;
248        for (size_t ii = 0; ii < size_t(message->msg_iovlen); ii++) {
249            auto nw = cb::net::send(sock,
250                                    message->msg_iov[ii].iov_base,
251                                    message->msg_iov[ii].iov_len,
252                                    0);
253            if (nw == -1) {
254                return (res == 0) ? -1 : res;
255            }
256
257            res += nw;
258        }
259
260        return res;
261    }
262
263    return ::sendmsg(sock, message, flags);
264}
265
266CBSOCKET_PUBLIC_API
267ssize_t sendto(SOCKET sock,
268               const void* buffer,
269               size_t length,
270               int flags,
271               const struct sockaddr* dest_addr,
272               socklen_t dest_len) {
273    if (should_log(sock)) {
274        throw std::runtime_error("cb::net::sendto: not implemented");
275    }
276
277#ifdef WIN32
278    return ::sendto(sock,
279                    static_cast<const char*>(buffer),
280                    int(length),
281                    flags,
282                    dest_addr,
283                    dest_len);
284#else
285    return ::sendto(sock, buffer, length, flags, dest_addr, dest_len);
286#endif
287}
288
289CBSOCKET_PUBLIC_API
290ssize_t recv(SOCKET sock, void* buffer, size_t length, int flags) {
291#ifdef WIN32
292    auto ret = ::recv(sock, static_cast<char*>(buffer), length, flags);
293#else
294    auto ret = ::recv(sock, buffer, length, flags);
295#endif
296
297    if (ret > 0 && should_log(sock)) {
298        log_it(sock, buffer, size_t(ret), "/r");
299    }
300
301    return ret;
302}
303
304CBSOCKET_PUBLIC_API
305ssize_t recvfrom(SOCKET sock,
306                 void* buffer,
307                 size_t length,
308                 int flags,
309                 struct sockaddr* address,
310                 socklen_t* address_len) {
311    if (should_log(sock)) {
312        throw std::runtime_error("cb::net::recvfrom: not implemented");
313    }
314
315#ifdef WIN32
316    return ::recvfrom(sock,
317                      static_cast<char*>(buffer),
318                      int(length),
319                      flags,
320                      address,
321                      address_len);
322#else
323    return ::recvfrom(sock, buffer, length, flags, address, address_len);
324#endif
325}
326
327CBSOCKET_PUBLIC_API
328ssize_t recvmsg(SOCKET sock, struct msghdr* message, int flags) {
329    if (logging) {
330        int res = 0;
331        for (size_t ii = 0; ii < size_t(message->msg_iovlen); ii++) {
332            auto nr = cb::net::recv(sock,
333                                    message->msg_iov[ii].iov_base,
334                                    message->msg_iov[ii].iov_len,
335                                    0);
336            if (nr == -1) {
337                return (res == 0) ? -1 : res;
338            }
339
340            res += nr;
341        }
342
343        return res;
344    }
345
346#ifdef WIN32
347    throw std::runtime_error("cb::net::recvmsg: Not implemented for win32");
348#else
349    return ::recvmsg(sock, message, flags);
350#endif
351}
352
353CBSOCKET_PUBLIC_API
354int socketpair(int domain, int type, int protocol, SOCKET socket_vector[2]) {
355    auto ret = evutil_socketpair(domain,
356                                 type,
357                                 protocol,
358                                 reinterpret_cast<evutil_socket_t*>(socket_vector));
359    if (ret == 0 && logging) {
360        cb::io::mkdirp(get_directory(socket_vector[0]));
361        cb::io::mkdirp(get_directory(socket_vector[1]));
362    }
363    return ret;
364}
365
366CBSOCKET_PUBLIC_API
367int set_socket_noblocking(SOCKET sock) {
368    return evutil_make_socket_nonblocking(sock);
369}
370
371} // namespace net
372} // namespace cb
373
374// C API which wraps into the C++ API
375
376CBSOCKET_PUBLIC_API
377int cb_closesocket(SOCKET s) {
378    return cb::net::closesocket(s);
379}
380
381CBSOCKET_PUBLIC_API
382int cb_get_socket_error() {
383    return cb::net::get_socket_error();
384}
385
386CBSOCKET_PUBLIC_API
387int cb_bind(SOCKET sock, const struct sockaddr* name, socklen_t namelen) {
388    return cb::net::bind(sock, name, namelen);
389}
390
391CBSOCKET_PUBLIC_API
392SOCKET cb_accept(SOCKET sock, struct sockaddr* addr, socklen_t* addrlen) {
393    return cb::net::accept(sock, addr, addrlen);
394}
395
396CBSOCKET_PUBLIC_API
397int cb_connect(SOCKET sock, const struct sockaddr* name, int namelen) {
398    return cb::net::connect(sock, name, namelen);
399}
400
401CBSOCKET_PUBLIC_API
402SOCKET cb_socket(int domain, int type, int protocol) {
403    return cb::net::socket(domain, type, protocol);
404}
405
406CBSOCKET_PUBLIC_API
407int cb_shutdown(SOCKET sock, int how) {
408    return cb::net::shutdown(sock, how);
409}
410
411CBSOCKET_PUBLIC_API
412ssize_t cb_send(SOCKET sock, const void* buffer, size_t length, int flags) {
413    return cb::net::send(sock, buffer, length, flags);
414}
415
416CBSOCKET_PUBLIC_API
417ssize_t cb_sendmsg(SOCKET sock, const struct msghdr* message, int flags) {
418    return cb::net::sendmsg(sock, message, flags);
419}
420
421CBSOCKET_PUBLIC_API
422ssize_t cb_sendto(SOCKET sock,
423                  const void* buffer,
424                  size_t length,
425                  int flags,
426                  const struct sockaddr* dest_addr,
427                  socklen_t dest_len) {
428    return cb::net::sendto(sock, buffer, length, flags, dest_addr, dest_len);
429}
430
431CBSOCKET_PUBLIC_API
432ssize_t cb_recv(SOCKET sock, void* buffer, size_t length, int flags) {
433    return cb::net::recv(sock, buffer, length, flags);
434}
435
436CBSOCKET_PUBLIC_API
437ssize_t cb_recvfrom(SOCKET sock,
438                    void* buffer,
439                    size_t length,
440                    int flags,
441                    struct sockaddr* address,
442                    socklen_t* address_len) {
443    return cb::net::recvfrom(sock, buffer, length, flags, address, address_len);
444}
445
446CBSOCKET_PUBLIC_API
447ssize_t cb_recvmsg(SOCKET sock, struct msghdr* message, int flags) {
448    return cb::net::recvmsg(sock, message, flags);
449}
450
451CBSOCKET_PUBLIC_API
452void cb_set_socket_logging(bool enable) {
453    cb::net::set_socket_logging(enable);
454}
455
456CBSOCKET_PUBLIC_API
457int cb_socketpair(int domain, int type, int protocol, SOCKET socket_vector[2]) {
458    return cb::net::socketpair(domain, type, protocol, socket_vector);
459}
460
461CBSOCKET_PUBLIC_API
462int cb_set_socket_noblocking(SOCKET sock) {
463    return cb::net::set_socket_noblocking(sock);
464}
465