1282471ddSBrett Lawson/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2282471ddSBrett Lawson/*
36bc025baSBrett Lawson *     Copyright 2014-2020 Couchbase, Inc.
4282471ddSBrett Lawson *
5282471ddSBrett Lawson *   Licensed under the Apache License, Version 2.0 (the "License");
6282471ddSBrett Lawson *   you may not use this file except in compliance with the License.
7282471ddSBrett Lawson *   You may obtain a copy of the License at
8282471ddSBrett Lawson *
9282471ddSBrett Lawson *       http://www.apache.org/licenses/LICENSE-2.0
10282471ddSBrett Lawson *
11282471ddSBrett Lawson *   Unless required by applicable law or agreed to in writing, software
12282471ddSBrett Lawson *   distributed under the License is distributed on an "AS IS" BASIS,
13282471ddSBrett Lawson *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14282471ddSBrett Lawson *   See the License for the specific language governing permissions and
15282471ddSBrett Lawson *   limitations under the License.
16282471ddSBrett Lawson */
17282471ddSBrett Lawson
186c98072aSBrett Lawson/**
196c98072aSBrett Lawson * Inline routines for common 'BSD'-style I/O for plugins.
206c98072aSBrett Lawson *
213e0810faSBrett Lawson * Include this file in your plugin and then call wire_lcb_bsd_impl2 on the
226c98072aSBrett Lawson * plugin instance.
236c98072aSBrett Lawson */
246c98072aSBrett Lawson
25d98a05aeSBrett Lawson#ifndef _WIN32
261a253065SBrett Lawson#include <netinet/in.h>
27d98a05aeSBrett Lawson#include <netinet/tcp.h>
28d98a05aeSBrett Lawson#endif
29d98a05aeSBrett Lawson
30f8bbdc9fSBrett Lawsonstatic void wire_lcb_bsd_impl2(lcb_bsd_procs *, int);
316c98072aSBrett Lawson
326c98072aSBrett Lawson#ifdef _WIN32
336c98072aSBrett Lawson#include "wsaerr-inl.c"
34f8bbdc9fSBrett Lawsonstatic int get_wserr(lcb_socket_t sock)
356c98072aSBrett Lawson{
366c98072aSBrett Lawson    DWORD error = WSAGetLastError();
376c98072aSBrett Lawson    int ext = 0;
386c98072aSBrett Lawson    int len = sizeof(ext);
396c98072aSBrett Lawson
406c98072aSBrett Lawson    /* Retrieves extended error status and clear */
416c98072aSBrett Lawson    getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&ext, &len);
426c98072aSBrett Lawson    return wsaerr_map_impl(error);
436c98072aSBrett Lawson}
446c98072aSBrett Lawson
45f8bbdc9fSBrett Lawsonstatic lcb_ssize_t recvv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)
466c98072aSBrett Lawson{
476c98072aSBrett Lawson    DWORD flags = 0, nr;
486c98072aSBrett Lawson    WSABUF *bufptr = (WSABUF *)iov;
496c98072aSBrett Lawson
506c98072aSBrett Lawson    if (WSARecv(sock, bufptr, niov, &nr, &flags, NULL, NULL) == SOCKET_ERROR) {
513e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = get_wserr(sock);
523e0810faSBrett Lawson        if (LCB_IOPS_ERRNO(iops) == ECONNRESET) {
536c98072aSBrett Lawson            return 0;
546c98072aSBrett Lawson        }
556c98072aSBrett Lawson        return -1;
566c98072aSBrett Lawson    }
576c98072aSBrett Lawson
586c98072aSBrett Lawson    (void)iops;
596c98072aSBrett Lawson    return (lcb_ssize_t)nr;
606c98072aSBrett Lawson}
616c98072aSBrett Lawson
62f8bbdc9fSBrett Lawsonstatic lcb_ssize_t recv_impl(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, int fl_unused)
636c98072aSBrett Lawson{
646c98072aSBrett Lawson    WSABUF iov;
656c98072aSBrett Lawson    iov.len = nbuf;
66bd3fff20SBrett Lawson    iov.buf = (CHAR *)buf;
676c98072aSBrett Lawson    (void)fl_unused;
686c98072aSBrett Lawson    return recvv_impl(iops, sock, (struct lcb_iovec_st *)&iov, 1);
696c98072aSBrett Lawson}
706c98072aSBrett Lawson
71f8bbdc9fSBrett Lawsonstatic lcb_ssize_t sendv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)
726c98072aSBrett Lawson{
736c98072aSBrett Lawson    DWORD nw, fl = 0;
746c98072aSBrett Lawson    WSABUF *bufptr = (WSABUF *)iov;
756c98072aSBrett Lawson    if (WSASend(sock, bufptr, niov, &nw, fl, NULL, NULL) == SOCKET_ERROR) {
763e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = get_wserr(sock);
776c98072aSBrett Lawson        return -1;
786c98072aSBrett Lawson    }
796c98072aSBrett Lawson    return (lcb_ssize_t)nw;
806c98072aSBrett Lawson}
816c98072aSBrett Lawson
82f8bbdc9fSBrett Lawsonstatic lcb_ssize_t send_impl(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, int flags)
836c98072aSBrett Lawson{
846c98072aSBrett Lawson    WSABUF iov;
85bd3fff20SBrett Lawson    iov.buf = (CHAR *)buf;
866c98072aSBrett Lawson    iov.len = nbuf;
876c98072aSBrett Lawson    (void)flags;
886c98072aSBrett Lawson    return sendv_impl(iops, sock, (struct lcb_iovec_st *)&iov, 1);
896c98072aSBrett Lawson}
906c98072aSBrett Lawson
916c98072aSBrett Lawson#else
92f8bbdc9fSBrett Lawsonstatic lcb_ssize_t recvv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)
936c98072aSBrett Lawson{
946c98072aSBrett Lawson    struct msghdr mh;
956c98072aSBrett Lawson    lcb_ssize_t ret;
966c98072aSBrett Lawson
976c98072aSBrett Lawson    memset(&mh, 0, sizeof(mh));
986c98072aSBrett Lawson    mh.msg_iov = (struct iovec *)iov;
996c98072aSBrett Lawson    mh.msg_iovlen = niov;
1006c98072aSBrett Lawson    ret = recvmsg(sock, &mh, 0);
1016c98072aSBrett Lawson    if (ret < 0) {
1023e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = errno;
1036c98072aSBrett Lawson    }
1046c98072aSBrett Lawson    return ret;
1056c98072aSBrett Lawson}
1066c98072aSBrett Lawson
107f8bbdc9fSBrett Lawsonstatic lcb_ssize_t recv_impl(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, int flags)
1086c98072aSBrett Lawson{
1096c98072aSBrett Lawson    lcb_ssize_t ret = recv(sock, buf, nbuf, flags);
1106c98072aSBrett Lawson    if (ret < 0) {
1113e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = errno;
1126c98072aSBrett Lawson    }
1136c98072aSBrett Lawson    return ret;
1146c98072aSBrett Lawson}
1156c98072aSBrett Lawson
116f8bbdc9fSBrett Lawsonstatic lcb_ssize_t sendv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)
1176c98072aSBrett Lawson{
1186c98072aSBrett Lawson    struct msghdr mh;
1196c98072aSBrett Lawson    lcb_ssize_t ret;
1206c98072aSBrett Lawson
1216c98072aSBrett Lawson    memset(&mh, 0, sizeof(mh));
1226c98072aSBrett Lawson    mh.msg_iov = (struct iovec *)iov;
1236c98072aSBrett Lawson    mh.msg_iovlen = niov;
1246c98072aSBrett Lawson    ret = sendmsg(sock, &mh, 0);
1256c98072aSBrett Lawson    if (ret < 0) {
1263e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = errno;
1276c98072aSBrett Lawson    }
1286c98072aSBrett Lawson    return ret;
1296c98072aSBrett Lawson}
1306c98072aSBrett Lawson
131f8bbdc9fSBrett Lawsonstatic lcb_ssize_t send_impl(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, int flags)
1326c98072aSBrett Lawson{
1336c98072aSBrett Lawson    lcb_ssize_t ret = send(sock, buf, nbuf, flags);
1346c98072aSBrett Lawson    if (ret < 0) {
1353e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = errno;
1366c98072aSBrett Lawson    }
1376c98072aSBrett Lawson    return ret;
1386c98072aSBrett Lawson}
1396c98072aSBrett Lawson
1406c98072aSBrett Lawson#endif
1416c98072aSBrett Lawson
1426c98072aSBrett Lawsonstatic int make_socket_nonblocking(lcb_socket_t sock)
1436c98072aSBrett Lawson{
1446c98072aSBrett Lawson#ifdef _WIN32
1456c98072aSBrett Lawson    u_long nonblocking = 1;
1466c98072aSBrett Lawson    if (ioctlsocket(sock, FIONBIO, &nonblocking) == SOCKET_ERROR) {
1476c98072aSBrett Lawson        return -1;
1486c98072aSBrett Lawson    }
1496c98072aSBrett Lawson#else
1506c98072aSBrett Lawson    int flags;
1516c98072aSBrett Lawson    if ((flags = fcntl(sock, F_GETFL, NULL)) < 0) {
1526c98072aSBrett Lawson        return -1;
1536c98072aSBrett Lawson    }
1546c98072aSBrett Lawson    if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
1556c98072aSBrett Lawson        return -1;
1566c98072aSBrett Lawson    }
1576c98072aSBrett Lawson#endif
1586c98072aSBrett Lawson    return 0;
1596c98072aSBrett Lawson}
1606c98072aSBrett Lawson
1613e0810faSBrett Lawson/* Declare */
162f8bbdc9fSBrett Lawsonstatic void close_impl(lcb_io_opt_t, lcb_socket_t);
1633e0810faSBrett Lawson
164f8bbdc9fSBrett Lawsonstatic lcb_socket_t socket_impl(lcb_io_opt_t iops, int domain, int type, int protocol)
1656c98072aSBrett Lawson{
1666c98072aSBrett Lawson    lcb_socket_t sock;
1676c98072aSBrett Lawson#ifdef _WIN32
1686c98072aSBrett Lawson    sock = (lcb_socket_t)WSASocket(domain, type, protocol, NULL, 0, 0);
1696c98072aSBrett Lawson#else
1706c98072aSBrett Lawson    sock = socket(domain, type, protocol);
1716c98072aSBrett Lawson#endif
1726c98072aSBrett Lawson    if (sock == INVALID_SOCKET) {
1733e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = errno;
1746c98072aSBrett Lawson    } else {
1756c98072aSBrett Lawson        if (make_socket_nonblocking(sock) != 0) {
1766c98072aSBrett Lawson#ifdef _WIN32
1773e0810faSBrett Lawson            LCB_IOPS_ERRNO(iops) = get_wserr(sock);
1786c98072aSBrett Lawson#else
1793e0810faSBrett Lawson            LCB_IOPS_ERRNO(iops) = errno;
1806c98072aSBrett Lawson#endif
1813e0810faSBrett Lawson            close_impl(iops, sock);
1826c98072aSBrett Lawson            sock = INVALID_SOCKET;
1836c98072aSBrett Lawson        }
1846c98072aSBrett Lawson    }
1856c98072aSBrett Lawson    return sock;
1866c98072aSBrett Lawson}
1876c98072aSBrett Lawson
188f8bbdc9fSBrett Lawsonstatic void close_impl(lcb_io_opt_t iops, lcb_socket_t sock)
1896c98072aSBrett Lawson{
1906c98072aSBrett Lawson    (void)iops;
1916c98072aSBrett Lawson#ifdef _WIN32
1926c98072aSBrett Lawson    closesocket(sock);
1936c98072aSBrett Lawson#else
1946c98072aSBrett Lawson    close(sock);
1956c98072aSBrett Lawson#endif
1966c98072aSBrett Lawson}
1976c98072aSBrett Lawson
198f8bbdc9fSBrett Lawsonstatic int connect_impl(lcb_io_opt_t iops, lcb_socket_t sock, const struct sockaddr *name, unsigned int namelen)
1996c98072aSBrett Lawson{
2006c98072aSBrett Lawson    int ret;
2016c98072aSBrett Lawson
2026c98072aSBrett Lawson#ifdef _WIN32
2036c98072aSBrett Lawson    ret = WSAConnect(sock, name, (int)namelen, NULL, NULL, NULL, NULL);
2046c98072aSBrett Lawson    if (ret == SOCKET_ERROR) {
2053e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = get_wserr(sock);
2066c98072aSBrett Lawson    }
2076c98072aSBrett Lawson#else
2086c98072aSBrett Lawson    ret = connect(sock, name, (socklen_t)namelen);
2096c98072aSBrett Lawson    if (ret < 0) {
2103e0810faSBrett Lawson        LCB_IOPS_ERRNO(iops) = errno;
2116c98072aSBrett Lawson    }
2126c98072aSBrett Lawson#endif
2136c98072aSBrett Lawson    return ret;
2146c98072aSBrett Lawson}
2156c98072aSBrett Lawson
21681aa54f1SBrett Lawson#if LCB_IOPROCS_VERSION >= 3
21781aa54f1SBrett Lawson
218f8bbdc9fSBrett Lawsonstatic int chkclosed_impl(lcb_io_opt_t iops, lcb_socket_t sock, int flags)
21981aa54f1SBrett Lawson{
22081aa54f1SBrett Lawson    char buf = 0;
22181aa54f1SBrett Lawson    int rv = 0;
22281aa54f1SBrett Lawson
22381aa54f1SBrett Lawson    (void)iops;
22481aa54f1SBrett Lawson
225f8bbdc9fSBrett LawsonGT_RETRY:
22681aa54f1SBrett Lawson    /* We can ignore flags for now, since both Windows and POSIX support MSG_PEEK */
22781aa54f1SBrett Lawson    rv = recv(sock, &buf, 1, MSG_PEEK);
22881aa54f1SBrett Lawson    if (rv == 1) {
22981aa54f1SBrett Lawson        if (flags & LCB_IO_SOCKCHECK_PEND_IS_ERROR) {
23081aa54f1SBrett Lawson            return LCB_IO_SOCKCHECK_STATUS_CLOSED;
23181aa54f1SBrett Lawson        } else {
23281aa54f1SBrett Lawson            return LCB_IO_SOCKCHECK_STATUS_OK;
23381aa54f1SBrett Lawson        }
23481aa54f1SBrett Lawson    } else if (rv == 0) {
23581aa54f1SBrett Lawson        /* Really closed! */
23681aa54f1SBrett Lawson        return LCB_IO_SOCKCHECK_STATUS_CLOSED;
23781aa54f1SBrett Lawson    } else {
23881aa54f1SBrett Lawson        int last_err;
239f8bbdc9fSBrett Lawson#ifdef _WIN32
24081aa54f1SBrett Lawson        last_err = get_wserr(sock);
241f8bbdc9fSBrett Lawson#else
24281aa54f1SBrett Lawson        last_err = errno;
243f8bbdc9fSBrett Lawson#endif
24481aa54f1SBrett Lawson
24581aa54f1SBrett Lawson        if (last_err == EINTR) {
24681aa54f1SBrett Lawson            goto GT_RETRY;
247f8bbdc9fSBrett Lawson        } else if (last_err == EWOULDBLOCK
248f8bbdc9fSBrett Lawson#if EWOULDBLOCK != EAGAIN
249f8bbdc9fSBrett Lawson                   || last_err == EAGAIN
250f8bbdc9fSBrett Lawson#endif
251f8bbdc9fSBrett Lawson        ) {
25281aa54f1SBrett Lawson            return LCB_IO_SOCKCHECK_STATUS_OK; /* Nothing to report. So we're good */
25381aa54f1SBrett Lawson        } else {
25481aa54f1SBrett Lawson            return LCB_IO_SOCKCHECK_STATUS_CLOSED;
25581aa54f1SBrett Lawson        }
25681aa54f1SBrett Lawson    }
25781aa54f1SBrett Lawson}
25881aa54f1SBrett Lawson#endif /* LCB_IOPROCS_VERSION >= 3 */
25981aa54f1SBrett Lawson
260d98a05aeSBrett Lawson#if LCB_IOPROCS_VERSION >= 4
261f8bbdc9fSBrett Lawsonstatic int cntl_getset_impl(lcb_io_opt_t io, lcb_socket_t sock, int mode, int oslevel, int osopt, int optsize,
262f8bbdc9fSBrett Lawson                            void *optval)
263d98a05aeSBrett Lawson{
264d98a05aeSBrett Lawson    int rv;
265f8bbdc9fSBrett Lawson#ifndef _WIN32
266d98a05aeSBrett Lawson    socklen_t dummy = optsize;
267f8bbdc9fSBrett Lawson#else
2688a5695e9SBrett Lawson    char dummy = optsize;
269f8bbdc9fSBrett Lawson#endif
270d98a05aeSBrett Lawson
271d98a05aeSBrett Lawson    if (mode == LCB_IO_CNTL_GET) {
272fab72700SBrett Lawson        rv = getsockopt(sock, oslevel, osopt, &dummy, (socklen_t *)optval);
273d98a05aeSBrett Lawson    } else {
274fab72700SBrett Lawson        rv = setsockopt(sock, oslevel, osopt, (const char *)optval, (socklen_t)optsize);
275d98a05aeSBrett Lawson    }
276d98a05aeSBrett Lawson    if (rv == 0) {
277d98a05aeSBrett Lawson        return 0;
278d98a05aeSBrett Lawson    } else {
279d98a05aeSBrett Lawson        int lasterr;
280f8bbdc9fSBrett Lawson#ifdef _WIN32
281d98a05aeSBrett Lawson        lasterr = get_wserr(sock);
282f8bbdc9fSBrett Lawson#else
283d98a05aeSBrett Lawson        lasterr = errno;
284f8bbdc9fSBrett Lawson#endif
285d98a05aeSBrett Lawson        LCB_IOPS_ERRNO(io) = lasterr;
286d98a05aeSBrett Lawson        return -1;
287d98a05aeSBrett Lawson    }
288d98a05aeSBrett Lawson}
289d98a05aeSBrett Lawson
290f8bbdc9fSBrett Lawsonstatic int cntl_impl(lcb_io_opt_t io, lcb_socket_t sock, int mode, int option, void *arg)
291d98a05aeSBrett Lawson{
292d98a05aeSBrett Lawson    switch (option) {
293f8bbdc9fSBrett Lawson        case LCB_IO_CNTL_TCP_NODELAY:
294f8bbdc9fSBrett Lawson            return cntl_getset_impl(io, sock, mode, IPPROTO_TCP, TCP_NODELAY, sizeof(int), arg);
295f8bbdc9fSBrett Lawson        case LCB_IO_CNTL_TCP_KEEPALIVE:
296f8bbdc9fSBrett Lawson            return cntl_getset_impl(io, sock, mode, SOL_SOCKET, SO_KEEPALIVE, sizeof(int), arg);
297f8bbdc9fSBrett Lawson        default:
298f8bbdc9fSBrett Lawson            LCB_IOPS_ERRNO(io) = ENOTSUP;
299f8bbdc9fSBrett Lawson            return -1;
300d98a05aeSBrett Lawson    }
301d98a05aeSBrett Lawson}
302d98a05aeSBrett Lawson#endif
303d98a05aeSBrett Lawson
3043e0810faSBrett Lawson#if !defined(LIBCOUCHBASE_INTERNAL) || defined(LCB_IOPS_V12_NO_DEPRECATE)
305f8bbdc9fSBrett Lawsonstatic void wire_lcb_bsd_impl(lcb_io_opt_t io)
3066c98072aSBrett Lawson{
3076c98072aSBrett Lawson    io->v.v0.recv = recv_impl;
3086c98072aSBrett Lawson    io->v.v0.recvv = recvv_impl;
3096c98072aSBrett Lawson    io->v.v0.send = send_impl;
3106c98072aSBrett Lawson    io->v.v0.sendv = sendv_impl;
3116c98072aSBrett Lawson    io->v.v0.socket = socket_impl;
3126c98072aSBrett Lawson    io->v.v0.connect = connect_impl;
3136c98072aSBrett Lawson    io->v.v0.close = close_impl;
31481aa54f1SBrett Lawson
31581aa54f1SBrett Lawson    /* Avoid annoying 'unused' warnings */
316f8bbdc9fSBrett Lawson    if (0) {
317f8bbdc9fSBrett Lawson        wire_lcb_bsd_impl2(NULL, 0);
318f8bbdc9fSBrett Lawson    }
31981aa54f1SBrett Lawson}
320f8bbdc9fSBrett Lawson#define lcb__wire0_nowarn()                                                                                            \
321f8bbdc9fSBrett Lawson    if (0) {                                                                                                           \
322f8bbdc9fSBrett Lawson        wire_lcb_bsd_impl(NULL);                                                                                       \
323f8bbdc9fSBrett Lawson    }
3243e0810faSBrett Lawson#else
3253e0810faSBrett Lawson#define lcb__wire0_nowarn()
3263e0810faSBrett Lawson#endif
32781aa54f1SBrett Lawson
32881aa54f1SBrett Lawson/** For plugins which use v2 or higher */
329f8bbdc9fSBrett Lawsonstatic void wire_lcb_bsd_impl2(lcb_bsd_procs *procs, int version)
33081aa54f1SBrett Lawson{
33181aa54f1SBrett Lawson    procs->recv = recv_impl;
33281aa54f1SBrett Lawson    procs->recvv = recvv_impl;
33381aa54f1SBrett Lawson    procs->send = send_impl;
33481aa54f1SBrett Lawson    procs->sendv = sendv_impl;
33581aa54f1SBrett Lawson    procs->socket0 = socket_impl;
33681aa54f1SBrett Lawson    procs->connect0 = connect_impl;
33781aa54f1SBrett Lawson    procs->close = close_impl;
33881aa54f1SBrett Lawson
339f8bbdc9fSBrett Lawson/* Check that this field exists at compile-time */
340f8bbdc9fSBrett Lawson#if LCB_IOPROCS_VERSION >= 3
34181aa54f1SBrett Lawson    if (version >= 3) {
34281aa54f1SBrett Lawson        procs->is_closed = chkclosed_impl;
34381aa54f1SBrett Lawson    }
344f8bbdc9fSBrett Lawson#endif
345f8bbdc9fSBrett Lawson#if LCB_IOPROCS_VERSION >= 4
346d98a05aeSBrett Lawson    if (version >= 4) {
347d98a05aeSBrett Lawson        procs->cntl = cntl_impl;
348d98a05aeSBrett Lawson    }
349f8bbdc9fSBrett Lawson#endif
3503e0810faSBrett Lawson    lcb__wire0_nowarn();
3516c98072aSBrett Lawson}