1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2014-2020 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 /**
19  * Inline routines for common 'BSD'-style I/O for plugins.
20  *
21  * Include this file in your plugin and then call wire_lcb_bsd_impl2 on the
22  * plugin instance.
23  */
24 
25 #ifndef _WIN32
26 #include <netinet/in.h>
27 #include <netinet/tcp.h>
28 #endif
29 
30 static void wire_lcb_bsd_impl2(lcb_bsd_procs *, int);
31 
32 #ifdef _WIN32
33 #include "wsaerr-inl.c"
get_wserr(lcb_socket_t sock)34 static int get_wserr(lcb_socket_t sock)
35 {
36     DWORD error = WSAGetLastError();
37     int ext = 0;
38     int len = sizeof(ext);
39 
40     /* Retrieves extended error status and clear */
41     getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&ext, &len);
42     return wsaerr_map_impl(error);
43 }
44 
recvv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)45 static lcb_ssize_t recvv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)
46 {
47     DWORD flags = 0, nr;
48     WSABUF *bufptr = (WSABUF *)iov;
49 
50     if (WSARecv(sock, bufptr, niov, &nr, &flags, NULL, NULL) == SOCKET_ERROR) {
51         LCB_IOPS_ERRNO(iops) = get_wserr(sock);
52         if (LCB_IOPS_ERRNO(iops) == ECONNRESET) {
53             return 0;
54         }
55         return -1;
56     }
57 
58     (void)iops;
59     return (lcb_ssize_t)nr;
60 }
61 
recv_impl(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, int fl_unused)62 static lcb_ssize_t recv_impl(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, int fl_unused)
63 {
64     WSABUF iov;
65     iov.len = nbuf;
66     iov.buf = (CHAR *)buf;
67     (void)fl_unused;
68     return recvv_impl(iops, sock, (struct lcb_iovec_st *)&iov, 1);
69 }
70 
sendv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)71 static lcb_ssize_t sendv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)
72 {
73     DWORD nw, fl = 0;
74     WSABUF *bufptr = (WSABUF *)iov;
75     if (WSASend(sock, bufptr, niov, &nw, fl, NULL, NULL) == SOCKET_ERROR) {
76         LCB_IOPS_ERRNO(iops) = get_wserr(sock);
77         return -1;
78     }
79     return (lcb_ssize_t)nw;
80 }
81 
send_impl(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, int flags)82 static lcb_ssize_t send_impl(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, int flags)
83 {
84     WSABUF iov;
85     iov.buf = (CHAR *)buf;
86     iov.len = nbuf;
87     (void)flags;
88     return sendv_impl(iops, sock, (struct lcb_iovec_st *)&iov, 1);
89 }
90 
91 #else
recvv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)92 static lcb_ssize_t recvv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)
93 {
94     struct msghdr mh;
95     lcb_ssize_t ret;
96 
97     memset(&mh, 0, sizeof(mh));
98     mh.msg_iov = (struct iovec *)iov;
99     mh.msg_iovlen = niov;
100     ret = recvmsg(sock, &mh, 0);
101     if (ret < 0) {
102         LCB_IOPS_ERRNO(iops) = errno;
103     }
104     return ret;
105 }
106 
recv_impl(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, int flags)107 static lcb_ssize_t recv_impl(lcb_io_opt_t iops, lcb_socket_t sock, void *buf, lcb_size_t nbuf, int flags)
108 {
109     lcb_ssize_t ret = recv(sock, buf, nbuf, flags);
110     if (ret < 0) {
111         LCB_IOPS_ERRNO(iops) = errno;
112     }
113     return ret;
114 }
115 
sendv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)116 static lcb_ssize_t sendv_impl(lcb_io_opt_t iops, lcb_socket_t sock, struct lcb_iovec_st *iov, lcb_size_t niov)
117 {
118     struct msghdr mh;
119     lcb_ssize_t ret;
120 
121     memset(&mh, 0, sizeof(mh));
122     mh.msg_iov = (struct iovec *)iov;
123     mh.msg_iovlen = niov;
124     ret = sendmsg(sock, &mh, 0);
125     if (ret < 0) {
126         LCB_IOPS_ERRNO(iops) = errno;
127     }
128     return ret;
129 }
130 
send_impl(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, int flags)131 static lcb_ssize_t send_impl(lcb_io_opt_t iops, lcb_socket_t sock, const void *buf, lcb_size_t nbuf, int flags)
132 {
133     lcb_ssize_t ret = send(sock, buf, nbuf, flags);
134     if (ret < 0) {
135         LCB_IOPS_ERRNO(iops) = errno;
136     }
137     return ret;
138 }
139 
140 #endif
141 
make_socket_nonblocking(lcb_socket_t sock)142 static int make_socket_nonblocking(lcb_socket_t sock)
143 {
144 #ifdef _WIN32
145     u_long nonblocking = 1;
146     if (ioctlsocket(sock, FIONBIO, &nonblocking) == SOCKET_ERROR) {
147         return -1;
148     }
149 #else
150     int flags;
151     if ((flags = fcntl(sock, F_GETFL, NULL)) < 0) {
152         return -1;
153     }
154     if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
155         return -1;
156     }
157 #endif
158     return 0;
159 }
160 
161 /* Declare */
162 static void close_impl(lcb_io_opt_t, lcb_socket_t);
163 
socket_impl(lcb_io_opt_t iops, int domain, int type, int protocol)164 static lcb_socket_t socket_impl(lcb_io_opt_t iops, int domain, int type, int protocol)
165 {
166     lcb_socket_t sock;
167 #ifdef _WIN32
168     sock = (lcb_socket_t)WSASocket(domain, type, protocol, NULL, 0, 0);
169 #else
170     sock = socket(domain, type, protocol);
171 #endif
172     if (sock == INVALID_SOCKET) {
173         LCB_IOPS_ERRNO(iops) = errno;
174     } else {
175         if (make_socket_nonblocking(sock) != 0) {
176 #ifdef _WIN32
177             LCB_IOPS_ERRNO(iops) = get_wserr(sock);
178 #else
179             LCB_IOPS_ERRNO(iops) = errno;
180 #endif
181             close_impl(iops, sock);
182             sock = INVALID_SOCKET;
183         }
184     }
185     return sock;
186 }
187 
close_impl(lcb_io_opt_t iops, lcb_socket_t sock)188 static void close_impl(lcb_io_opt_t iops, lcb_socket_t sock)
189 {
190     (void)iops;
191 #ifdef _WIN32
192     closesocket(sock);
193 #else
194     close(sock);
195 #endif
196 }
197 
connect_impl(lcb_io_opt_t iops, lcb_socket_t sock, const struct sockaddr *name, unsigned int namelen)198 static int connect_impl(lcb_io_opt_t iops, lcb_socket_t sock, const struct sockaddr *name, unsigned int namelen)
199 {
200     int ret;
201 
202 #ifdef _WIN32
203     ret = WSAConnect(sock, name, (int)namelen, NULL, NULL, NULL, NULL);
204     if (ret == SOCKET_ERROR) {
205         LCB_IOPS_ERRNO(iops) = get_wserr(sock);
206     }
207 #else
208     ret = connect(sock, name, (socklen_t)namelen);
209     if (ret < 0) {
210         LCB_IOPS_ERRNO(iops) = errno;
211     }
212 #endif
213     return ret;
214 }
215 
216 #if LCB_IOPROCS_VERSION >= 3
217 
chkclosed_impl(lcb_io_opt_t iops, lcb_socket_t sock, int flags)218 static int chkclosed_impl(lcb_io_opt_t iops, lcb_socket_t sock, int flags)
219 {
220     char buf = 0;
221     int rv = 0;
222 
223     (void)iops;
224 
225 GT_RETRY:
226     /* We can ignore flags for now, since both Windows and POSIX support MSG_PEEK */
227     rv = recv(sock, &buf, 1, MSG_PEEK);
228     if (rv == 1) {
229         if (flags & LCB_IO_SOCKCHECK_PEND_IS_ERROR) {
230             return LCB_IO_SOCKCHECK_STATUS_CLOSED;
231         } else {
232             return LCB_IO_SOCKCHECK_STATUS_OK;
233         }
234     } else if (rv == 0) {
235         /* Really closed! */
236         return LCB_IO_SOCKCHECK_STATUS_CLOSED;
237     } else {
238         int last_err;
239 #ifdef _WIN32
240         last_err = get_wserr(sock);
241 #else
242         last_err = errno;
243 #endif
244 
245         if (last_err == EINTR) {
246             goto GT_RETRY;
247         } else if (last_err == EWOULDBLOCK
248 #if EWOULDBLOCK != EAGAIN
249                    || last_err == EAGAIN
250 #endif
251         ) {
252             return LCB_IO_SOCKCHECK_STATUS_OK; /* Nothing to report. So we're good */
253         } else {
254             return LCB_IO_SOCKCHECK_STATUS_CLOSED;
255         }
256     }
257 }
258 #endif /* LCB_IOPROCS_VERSION >= 3 */
259 
260 #if LCB_IOPROCS_VERSION >= 4
cntl_getset_impl(lcb_io_opt_t io, lcb_socket_t sock, int mode, int oslevel, int osopt, int optsize, void *optval)261 static int cntl_getset_impl(lcb_io_opt_t io, lcb_socket_t sock, int mode, int oslevel, int osopt, int optsize,
262                             void *optval)
263 {
264     int rv;
265 #ifndef _WIN32
266     socklen_t dummy = optsize;
267 #else
268     char dummy = optsize;
269 #endif
270 
271     if (mode == LCB_IO_CNTL_GET) {
272         rv = getsockopt(sock, oslevel, osopt, &dummy, (socklen_t *)optval);
273     } else {
274         rv = setsockopt(sock, oslevel, osopt, (const char *)optval, (socklen_t)optsize);
275     }
276     if (rv == 0) {
277         return 0;
278     } else {
279         int lasterr;
280 #ifdef _WIN32
281         lasterr = get_wserr(sock);
282 #else
283         lasterr = errno;
284 #endif
285         LCB_IOPS_ERRNO(io) = lasterr;
286         return -1;
287     }
288 }
289 
cntl_impl(lcb_io_opt_t io, lcb_socket_t sock, int mode, int option, void *arg)290 static int cntl_impl(lcb_io_opt_t io, lcb_socket_t sock, int mode, int option, void *arg)
291 {
292     switch (option) {
293         case LCB_IO_CNTL_TCP_NODELAY:
294             return cntl_getset_impl(io, sock, mode, IPPROTO_TCP, TCP_NODELAY, sizeof(int), arg);
295         case LCB_IO_CNTL_TCP_KEEPALIVE:
296             return cntl_getset_impl(io, sock, mode, SOL_SOCKET, SO_KEEPALIVE, sizeof(int), arg);
297         default:
298             LCB_IOPS_ERRNO(io) = ENOTSUP;
299             return -1;
300     }
301 }
302 #endif
303 
304 #if !defined(LIBCOUCHBASE_INTERNAL) || defined(LCB_IOPS_V12_NO_DEPRECATE)
wire_lcb_bsd_impl(lcb_io_opt_t io)305 static void wire_lcb_bsd_impl(lcb_io_opt_t io)
306 {
307     io->v.v0.recv = recv_impl;
308     io->v.v0.recvv = recvv_impl;
309     io->v.v0.send = send_impl;
310     io->v.v0.sendv = sendv_impl;
311     io->v.v0.socket = socket_impl;
312     io->v.v0.connect = connect_impl;
313     io->v.v0.close = close_impl;
314 
315     /* Avoid annoying 'unused' warnings */
316     if (0) {
317         wire_lcb_bsd_impl2(NULL, 0);
318     }
319 }
320 #define lcb__wire0_nowarn()                                                                                            \
321     if (0) {                                                                                                           \
322         wire_lcb_bsd_impl(NULL);                                                                                       \
323     }
324 #else
325 #define lcb__wire0_nowarn()
326 #endif
327 
328 /** For plugins which use v2 or higher */
wire_lcb_bsd_impl2(lcb_bsd_procs *procs, int version)329 static void wire_lcb_bsd_impl2(lcb_bsd_procs *procs, int version)
330 {
331     procs->recv = recv_impl;
332     procs->recvv = recvv_impl;
333     procs->send = send_impl;
334     procs->sendv = sendv_impl;
335     procs->socket0 = socket_impl;
336     procs->connect0 = connect_impl;
337     procs->close = close_impl;
338 
339 /* Check that this field exists at compile-time */
340 #if LCB_IOPROCS_VERSION >= 3
341     if (version >= 3) {
342         procs->is_closed = chkclosed_impl;
343     }
344 #endif
345 #if LCB_IOPROCS_VERSION >= 4
346     if (version >= 4) {
347         procs->cntl = cntl_impl;
348     }
349 #endif
350     lcb__wire0_nowarn();
351 }
352