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