]> git.meshlink.io Git - catta/blob - src/compat/windows/wincompat.c
add wsa error code to errnostrsocket for easier error lookup
[catta] / src / compat / windows / wincompat.c
1 #include "wincompat.h"
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <assert.h>
5 #include <stdint.h>
6 #include <stdio.h>
7
8 #include <catta/log.h>
9
10 // helper: convert WSAGetLastError() to an errno constant
11 static int wsa_errno(void)
12 {
13     switch(WSAGetLastError()) {
14         case WSAEACCES:         return EACCES;
15         case WSAECONNRESET:     return ECONNRESET;
16         case WSAEFAULT:         return EFAULT;
17         case WSAEINPROGRESS:    return EINPROGRESS;
18         case WSAEINTR:          return EINTR;
19         case WSAEINVAL:         return EINVAL;
20         case WSAEMSGSIZE:       return EMSGSIZE;
21         case WSAENETDOWN:       return ENETDOWN;
22         case WSAENETRESET:      return ENETRESET;
23         case WSAENOBUFS:        return ENOBUFS;
24         case WSAENOTCONN:       return ENOTCONN;
25         case WSAENOTSOCK:       return ENOTSOCK;
26         case WSAEOPNOTSUPP:     return EOPNOTSUPP;
27         case WSAESHUTDOWN:      return ESHUTDOWN;
28         case WSAETIMEDOUT:      return ETIMEDOUT;
29         case WSAEWOULDBLOCK:    return EWOULDBLOCK;
30         default:
31             return EINVAL;
32     }
33 }
34
35 void winsock_init(void)
36 {
37     WSADATA wsa;
38     int error;
39
40     if((error = WSAStartup(MAKEWORD(2,2), &wsa)) != 0)
41         catta_log_error("WSAStartup() failed: %d", error);
42 }
43
44 void winsock_exit(void)
45 {
46     if(WSACleanup() == SOCKET_ERROR)
47         catta_log_warn("WSACleanup() failed: %d", WSAGetLastError());
48 }
49
50 char *errnostrsocket(void)
51 {
52     static char buf[256];
53
54     int err = WSAGetLastError();
55     int len = snprintf(buf, sizeof(buf), "[%i] ", err);
56     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
57                   NULL, err, 0, buf + len, sizeof(buf) - len, NULL);
58
59     return buf;
60 }
61
62 ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
63 {
64     LPFN_WSARECVMSG WSARecvMsg = NULL;
65     GUID wsaid = WSAID_WSARECVMSG;
66     DWORD b;
67
68     DWORD bytesrcvd;
69     WSAMSG wsamsg;
70     size_t i;
71     int r;
72
73     // size_t is larger than DWORD on 64bit
74     if(msg->msg_iovlen > UINT32_MAX) {
75         errno = EINVAL;
76         return -1;
77     }
78
79     // obtain the function pointer to WSARecvMsg
80     r = WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
81                  &wsaid, sizeof(wsaid), &WSARecvMsg, sizeof(WSARecvMsg),
82                  &b, NULL, NULL);
83     if(r == SOCKET_ERROR) {
84         errno = wsa_errno();
85         return -1;
86     }
87     assert(b == sizeof(WSARecvMsg));
88     assert(WSARecvMsg != NULL);
89
90     // convert msghdr to WSAMSG structure
91     wsamsg.name = msg->msg_name;
92     wsamsg.namelen = msg->msg_namelen;
93     wsamsg.lpBuffers = malloc(msg->msg_iovlen * sizeof(WSABUF));
94     wsamsg.dwBufferCount = msg->msg_iovlen;
95     wsamsg.Control.len = msg->msg_controllen;
96     wsamsg.Control.buf = msg->msg_control;
97     wsamsg.dwFlags = (DWORD)flags;
98
99     // all flags that fit into dwFlags also fit through the flags argument
100     assert(sizeof(DWORD) <= sizeof(flags));
101
102     if(wsamsg.lpBuffers == NULL) {
103         // malloc will have set errno
104         return -1;
105     }
106
107     // re-wrap iovecs as WSABUFs
108     for(i=0; i<msg->msg_iovlen; i++) {
109         // size_t vs. u_long
110         if(msg->msg_iov[i].iov_len > ULONG_MAX) {
111             free(wsamsg.lpBuffers);
112             errno = EINVAL;
113             return -1;
114         }
115
116         wsamsg.lpBuffers[i].len = msg->msg_iov[i].iov_len;
117         wsamsg.lpBuffers[i].buf = msg->msg_iov[i].iov_base;
118     }
119
120     r = WSARecvMsg(sockfd, &wsamsg, &bytesrcvd, NULL, NULL);
121
122     // the allocated WSABUF wrappers are no longer needed
123     free(wsamsg.lpBuffers);
124
125     if(r == SOCKET_ERROR) {
126         // XXX do we need special handling for ENETRESET, EMSGSIZE, ETIMEDOUT?
127         errno = wsa_errno();
128         return -1;
129     }
130
131     // DWORD has one bit more than ssize_t on 32bit
132     // XXX check for this condition before the WSARecvMsg call
133     if(bytesrcvd > SSIZE_MAX) {
134         errno = EINVAL;
135         return -1;
136     }
137
138     // transfer results from wsamsg to msg
139     // NB: the data and control buffers are shared
140     msg->msg_controllen = wsamsg.Control.len;
141     msg->msg_flags = (int)wsamsg.dwFlags;
142         // all flags that fit into dwFlags also fit into msg_flags (see above)
143
144     return bytesrcvd;
145 }
146
147 ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
148 {
149     LPFN_WSASENDMSG WSASendMsg = NULL;
150     GUID wsaid = WSAID_WSASENDMSG;
151     DWORD b;
152
153     DWORD bytessent;
154     WSAMSG wsamsg;
155     size_t i;
156     int r;
157
158     // size_t is larger than DWORD on 64bit
159     if(msg->msg_iovlen > UINT32_MAX) {
160         errno = EINVAL;
161         return -1;
162     }
163
164     // obtain the function pointer to WSASendMsg
165     r = WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
166                  &wsaid, sizeof(wsaid), &WSASendMsg, sizeof(WSASendMsg),
167                  &b, NULL, NULL);
168     if(r == SOCKET_ERROR) {
169         errno = wsa_errno();
170         return -1;
171     }
172     assert(b == sizeof(WSASendMsg));
173     assert(WSASendMsg != NULL);
174
175     // convert msghdr to WSAMSG structure
176     wsamsg.name = msg->msg_name;
177     wsamsg.namelen = msg->msg_namelen;
178     wsamsg.lpBuffers = malloc(msg->msg_iovlen * sizeof(WSABUF));
179     wsamsg.dwBufferCount = msg->msg_iovlen;
180     wsamsg.Control.len = msg->msg_controllen;
181     wsamsg.Control.buf = msg->msg_control;
182     wsamsg.dwFlags = 0; // ignored
183
184     if(wsamsg.lpBuffers == NULL) {
185         // malloc will have set errno
186         return -1;
187     }
188
189     // re-wrap iovecs as WSABUFs
190     for(i=0; i<msg->msg_iovlen; i++) {
191         // size_t vs. u_long
192         if(msg->msg_iov[i].iov_len > ULONG_MAX) {
193             free(wsamsg.lpBuffers);
194             errno = EINVAL;
195             return -1;
196         }
197
198         wsamsg.lpBuffers[i].len = msg->msg_iov[i].iov_len;
199         wsamsg.lpBuffers[i].buf = msg->msg_iov[i].iov_base;
200     }
201
202     r = WSASendMsg(sockfd, &wsamsg, flags, &bytessent, NULL, NULL);
203
204     // the allocated WSABUF wrappers are no longer needed
205     free(wsamsg.lpBuffers);
206
207     if(r == SOCKET_ERROR) {
208         // XXX do we need special handling for ENETRESET, ETIMEDOUT?
209         errno = wsa_errno();
210         return -1;
211     }
212
213     // DWORD has one bit more than ssize_t on 32bit
214     // XXX check for this condition before sending anything
215     if(bytessent > SSIZE_MAX) {
216         errno = EINVAL;
217         return -1;
218     }
219
220     return bytessent;
221 }
222
223 int ioctl(int d, unsigned long request, int *p)
224 {
225     u_long arg = *p;
226
227     if(ioctlsocket(d, request, &arg) == SOCKET_ERROR) {
228         errno = wsa_errno();
229         return -1;
230     }
231
232     if(arg > INT_MAX) {
233         errno = EINVAL;
234         return -1;
235     }
236
237     *p = arg;
238     return 0;
239 }
240
241 int pipe(int pipefd[2])
242 {
243     int lsock = (int)INVALID_SOCKET;
244     struct sockaddr_in laddr;
245     socklen_t laddrlen = sizeof(laddr);
246
247     pipefd[0] = pipefd[1] = (int)INVALID_SOCKET;
248
249     // bind a listening socket to a TCP port on localhost
250     laddr.sin_family = AF_INET;
251     laddr.sin_port = 0;
252     laddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
253     if((lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR)
254         goto fail;
255     if(bind(lsock, (struct sockaddr *)&laddr, sizeof(laddr)) == SOCKET_ERROR)
256         goto fail;
257     if(listen(lsock, 1) == SOCKET_ERROR)
258         goto fail;
259
260     // determine which address (i.e. port) we got bound to
261     if(getsockname(lsock, (struct sockaddr *)&laddr, &laddrlen) == SOCKET_ERROR)
262         goto fail;
263     assert(laddrlen == sizeof(laddr));
264     laddr.sin_family = AF_INET;
265     laddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
266
267     // connect and accept
268     if((pipefd[0] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR)
269         goto fail;
270     if(connect(pipefd[0], (const struct sockaddr *)&laddr, sizeof(laddr)) == SOCKET_ERROR)
271         goto fail;
272     if((pipefd[1] = accept(lsock, NULL, NULL)) == SOCKET_ERROR)
273         goto fail;
274
275     // close the listener
276     closesocket(lsock);
277
278     return 0;
279
280 fail:
281     errno = wsa_errno();
282     closesocket(pipefd[0]);
283     closesocket(lsock);
284     return -1;
285 }
286
287 int uname(struct utsname *buf)
288 {
289     SYSTEM_INFO si;
290     const char *arch = "unknown";
291
292     memset(buf, 0, sizeof(struct utsname));
293
294     // operating system
295     strncpy(buf->sysname, "Windows", sizeof(buf->sysname)-1);
296     strncpy(buf->release, "unknown", sizeof(buf->sysname)-1);   // we don't need it
297     strncpy(buf->version, "unknown", sizeof(buf->sysname)-1);   // we don't need it
298
299     // computer (node) name
300     DWORD nodename_size = sizeof(buf->nodename)-1;
301     if(GetComputerName(buf->nodename, &nodename_size) == 0) {
302         errno = EFAULT;
303         return -1;
304     }
305
306     // hardware type
307     GetSystemInfo(&si);
308     switch(si.wProcessorArchitecture) {
309         case PROCESSOR_ARCHITECTURE_AMD64: arch = "amd64"; break;
310         case PROCESSOR_ARCHITECTURE_ARM:   arch = "arm";   break;
311         case PROCESSOR_ARCHITECTURE_IA64:  arch = "ia64";  break;
312         case PROCESSOR_ARCHITECTURE_INTEL: arch = "x86";   break;
313         default: arch = "unknown";
314     }
315     strncpy(buf->machine, arch, sizeof(buf->machine)-1);
316
317     return 0;
318 }