1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2013-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  * New-Style v2 plugin for Windows, Using IOCP.
20  * This file contains various utility functions used by the plugin
21  * @author Mark Nunberg
22  */
23 
24 #include "iocp_iops.h"
25 #include <sys/types.h>
26 #include <sys/timeb.h>
27 #include <time.h>
28 #include "config.h"
29 #include <libcouchbase/plugins/io/wsaerr-inl.c>
30 
31 #if defined(__MINGW32__) && !defined(_ftime_s)
32 #define _ftime_s _ftime /** Mingw doens't have the _s variant */
33 #endif
34 
iocp_w32err_2errno(DWORD error)35 int iocp_w32err_2errno(DWORD error)
36 {
37     return wsaerr_map_impl(error);
38 }
39 
iocp_set_last_error(lcb_io_opt_t io, SOCKET sock)40 DWORD iocp_set_last_error(lcb_io_opt_t io, SOCKET sock)
41 {
42     int werr = GetLastError();
43     io->v.v2.error = iocp_w32err_2errno(werr);
44     return werr;
45 }
46 
iocp_micros(void)47 lcb_uint32_t iocp_micros(void)
48 {
49     return (lcb_uint32_t)(gethrtime() / 1000);
50 }
51 
iocp_initialize_connectex(SOCKET sock)52 LPFN_CONNECTEX iocp_initialize_connectex(SOCKET sock)
53 {
54     LPFN_CONNECTEX ret = NULL;
55     DWORD dwBytes;
56     GUID ce_guid = WSAID_CONNECTEX;
57 
58     WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &ce_guid, sizeof(ce_guid), &ret, sizeof(&ret), &dwBytes, NULL,
59              NULL);
60 
61     return ret;
62 }
63 
iocp_just_scheduled(iocp_t *io, iocp_overlapped_t *ol, int status)64 int iocp_just_scheduled(iocp_t *io, iocp_overlapped_t *ol, int status)
65 {
66     DWORD err = GetLastError();
67     IOCP_LOG(IOCP_TRACE, "Pending count: %d", io->n_iopending);
68     if ((status != 0 && err == WSA_IO_PENDING) || status == 0) {
69         io->n_iopending++;
70         ol->sd->refcount++;
71         return 0;
72     }
73 
74     /**
75      * Otherwise, there's something wrong
76      */
77     IOCP_LOG(IOCP_ERR, "Got non-harmless error for %p: %d", ol, (int)err);
78     io->base.v.v2.error = iocp_w32err_2errno(err);
79     return -1;
80 }
81 
iocp_socket_decref(iocp_t *io, iocp_sockdata_t *sd)82 void iocp_socket_decref(iocp_t *io, iocp_sockdata_t *sd)
83 {
84     if (--sd->refcount) {
85         return;
86     }
87 
88     if (sd->sSocket != INVALID_SOCKET) {
89         closesocket(sd->sSocket);
90     }
91 
92     lcb_list_delete(&sd->list);
93 
94     (void)io;
95     free(sd);
96 }
97 
iocp_on_dequeued(iocp_t *io, iocp_sockdata_t *sd, int action)98 void iocp_on_dequeued(iocp_t *io, iocp_sockdata_t *sd, int action)
99 {
100     IOCP_LOG(IOCP_TRACE, "Dequeing. A=%d, Pending=%d", action, io->n_iopending);
101     iocp_socket_decref(io, sd);
102 }
103 
104 /**This following function was copied from libuv.
105  * See http://github.com/joyent/libuv for more details */
iocp_overlapped_status(OVERLAPPED *lpOverlapped)106 int iocp_overlapped_status(OVERLAPPED *lpOverlapped)
107 {
108     NTSTATUS status = (NTSTATUS)lpOverlapped->Internal;
109     switch (status) {
110         case 0:
111             return ERROR_SUCCESS;
112 
113         case STATUS_PENDING:
114             return ERROR_IO_PENDING;
115 
116         case STATUS_INVALID_HANDLE:
117         case STATUS_OBJECT_TYPE_MISMATCH:
118             return WSAENOTSOCK;
119 
120         case STATUS_INSUFFICIENT_RESOURCES:
121         case STATUS_PAGEFILE_QUOTA:
122         case STATUS_COMMITMENT_LIMIT:
123         case STATUS_WORKING_SET_QUOTA:
124         case STATUS_NO_MEMORY:
125         case STATUS_CONFLICTING_ADDRESSES:
126         case STATUS_QUOTA_EXCEEDED:
127         case STATUS_TOO_MANY_PAGING_FILES:
128         case STATUS_REMOTE_RESOURCES:
129         case STATUS_TOO_MANY_ADDRESSES:
130             return WSAENOBUFS;
131 
132         case STATUS_SHARING_VIOLATION:
133         case STATUS_ADDRESS_ALREADY_EXISTS:
134             return WSAEADDRINUSE;
135 
136         case STATUS_LINK_TIMEOUT:
137         case STATUS_IO_TIMEOUT:
138         case STATUS_TIMEOUT:
139             return WSAETIMEDOUT;
140 
141         case STATUS_GRACEFUL_DISCONNECT:
142             return WSAEDISCON;
143 
144         case STATUS_REMOTE_DISCONNECT:
145         case STATUS_CONNECTION_RESET:
146         case STATUS_LINK_FAILED:
147         case STATUS_CONNECTION_DISCONNECTED:
148         case STATUS_PORT_UNREACHABLE:
149         case STATUS_HOPLIMIT_EXCEEDED:
150             return WSAECONNRESET;
151 
152         case STATUS_LOCAL_DISCONNECT:
153         case STATUS_TRANSACTION_ABORTED:
154         case STATUS_CONNECTION_ABORTED:
155             return WSAECONNABORTED;
156 
157         case STATUS_BAD_NETWORK_PATH:
158         case STATUS_NETWORK_UNREACHABLE:
159         case STATUS_PROTOCOL_UNREACHABLE:
160             return WSAENETUNREACH;
161 
162         case STATUS_HOST_UNREACHABLE:
163             return WSAEHOSTUNREACH;
164 
165         case STATUS_CANCELLED:
166         case STATUS_REQUEST_ABORTED:
167             return WSAEINTR;
168 
169         case STATUS_BUFFER_OVERFLOW:
170         case STATUS_INVALID_BUFFER_SIZE:
171             return WSAEMSGSIZE;
172 
173         case STATUS_BUFFER_TOO_SMALL:
174         case STATUS_ACCESS_VIOLATION:
175             return WSAEFAULT;
176 
177         case STATUS_DEVICE_NOT_READY:
178         case STATUS_REQUEST_NOT_ACCEPTED:
179             return WSAEWOULDBLOCK;
180 
181         case STATUS_INVALID_NETWORK_RESPONSE:
182         case STATUS_NETWORK_BUSY:
183         case STATUS_NO_SUCH_DEVICE:
184         case STATUS_NO_SUCH_FILE:
185         case STATUS_OBJECT_PATH_NOT_FOUND:
186         case STATUS_OBJECT_NAME_NOT_FOUND:
187         case STATUS_UNEXPECTED_NETWORK_ERROR:
188             return WSAENETDOWN;
189 
190         case STATUS_INVALID_CONNECTION:
191             return WSAENOTCONN;
192 
193         case STATUS_REMOTE_NOT_LISTENING:
194         case STATUS_CONNECTION_REFUSED:
195             return WSAECONNREFUSED;
196 
197         case STATUS_PIPE_DISCONNECTED:
198             return WSAESHUTDOWN;
199 
200         case STATUS_INVALID_ADDRESS:
201         case STATUS_INVALID_ADDRESS_COMPONENT:
202             return WSAEADDRNOTAVAIL;
203 
204         case STATUS_NOT_SUPPORTED:
205         case STATUS_NOT_IMPLEMENTED:
206             return WSAEOPNOTSUPP;
207 
208         case STATUS_ACCESS_DENIED:
209             return WSAEACCES;
210 
211         default:
212             if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) &&
213                 (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING))) {
214                 /* It's a windows error that has been previously mapped to an */
215                 /* ntstatus code. */
216                 return (DWORD)(status & 0xffff);
217             } else {
218                 /* The default fallback for unmappable ntstatus codes. */
219                 return WSAEINVAL;
220             }
221     }
222 }
223