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