]> git.meshlink.io Git - catta/blob - src/compat/windows/wincompat.c
1a05d9f58551a91589472cf95d65211c46db5dca
[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 // helper: convert WSAGetLastError() to an errno constant
8 static int wsa_errno(void)
9 {
10     switch(WSAGetLastError()) {
11         case WSAEACCES:         return EACCES;
12         case WSAECONNRESET:     return ECONNRESET;
13         case WSAEFAULT:         return EFAULT;
14         case WSAEINPROGRESS:    return EINPROGRESS;
15         case WSAEINTR:          return EINTR;
16         case WSAEINVAL:         return EINVAL;
17         case WSAEMSGSIZE:       return EMSGSIZE;
18         case WSAENETDOWN:       return ENETDOWN;
19         case WSAENETRESET:      return ENETRESET;
20         case WSAENOBUFS:        return ENOBUFS;
21         case WSAENOTCONN:       return ENOTCONN;
22         case WSAENOTSOCK:       return ENOTSOCK;
23         case WSAEOPNOTSUPP:     return EOPNOTSUPP;
24         case WSAESHUTDOWN:      return ESHUTDOWN;
25         case WSAETIMEDOUT:      return ETIMEDOUT;
26         case WSAEWOULDBLOCK:    return EWOULDBLOCK;
27         default:
28             return EINVAL;
29     }
30 }
31
32 ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
33 {
34     LPFN_WSARECVMSG WSARecvMsg = NULL;
35     GUID wsaid = WSAID_WSARECVMSG;
36     DWORD b;
37
38     DWORD bytesrcvd;
39     WSAMSG wsamsg;
40     size_t i;
41     int r;
42
43     // size_t is larger than DWORD on 64bit
44     if(msg->msg_iovlen > UINT32_MAX) {
45         errno = EINVAL;
46         return -1;
47     }
48
49     // obtain the function pointer to WSARecvMsg
50     r = WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
51                  &wsaid, sizeof(wsaid), &WSARecvMsg, sizeof(WSARecvMsg),
52                  &b, NULL, NULL);
53     if(r == SOCKET_ERROR) {
54         errno = wsa_errno();
55         return -1;
56     }
57     assert(b == sizeof(WSARecvMsg));
58     assert(WSARecvMsg != NULL);
59
60     // convert msghdr to WSAMSG structure
61     wsamsg.name = msg->msg_name;
62     wsamsg.namelen = msg->msg_namelen;
63     wsamsg.lpBuffers = malloc(msg->msg_iovlen * sizeof(WSABUF));
64     wsamsg.dwBufferCount = msg->msg_iovlen;
65     wsamsg.Control.len = msg->msg_controllen;
66     wsamsg.Control.buf = msg->msg_control;
67     wsamsg.dwFlags = (DWORD)flags;
68
69     // all flags that fit into dwFlags also fit through the flags argument
70     assert(sizeof(DWORD) <= sizeof(flags));
71
72     if(wsamsg.lpBuffers == NULL) {
73         // malloc will have set errno
74         return -1;
75     }
76
77     // re-wrap iovecs as WSABUFs
78     for(i=0; i<msg->msg_iovlen; i++) {
79         // size_t vs. u_long
80         if(msg->msg_iov[i].iov_len > ULONG_MAX) {
81             free(wsamsg.lpBuffers);
82             errno = EINVAL;
83             return -1;
84         }
85
86         wsamsg.lpBuffers[i].len = msg->msg_iov[i].iov_len;
87         wsamsg.lpBuffers[i].buf = msg->msg_iov[i].iov_base;
88     }
89
90     r = WSARecvMsg(sockfd, &wsamsg, &bytesrcvd, NULL, NULL);
91
92     // the allocated WSABUF wrappers are no longer needed
93     free(wsamsg.lpBuffers);
94
95     if(r == SOCKET_ERROR) {
96         // XXX do we need special handling for ENETRESET, EMSGSIZE, ETIMEDOUT?
97         errno = wsa_errno();
98         return -1;
99     }
100
101     // DWORD has one bit more than ssize_t on 32bit
102     // XXX check for this condition before the WSARecvMsg call
103     if(bytesrcvd > SSIZE_MAX) {
104         errno = EINVAL;
105         return -1;
106     }
107
108     // transfer results from wsamsg to msg
109     // NB: the data and control buffers are shared
110     msg->msg_controllen = wsamsg.Control.len;
111     msg->msg_flags = (int)wsamsg.dwFlags;
112         // all flags that fit into dwFlags also fit into msg_flags (see above)
113
114     return bytesrcvd;
115 }
116
117 ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
118 {
119     LPFN_WSASENDMSG WSASendMsg = NULL;
120     GUID wsaid = WSAID_WSASENDMSG;
121     DWORD b;
122
123     DWORD bytessent;
124     WSAMSG wsamsg;
125     size_t i;
126     int r;
127
128     // size_t is larger than DWORD on 64bit
129     if(msg->msg_iovlen > UINT32_MAX) {
130         errno = EINVAL;
131         return -1;
132     }
133
134     // obtain the function pointer to WSASendMsg
135     r = WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
136                  &wsaid, sizeof(wsaid), &WSASendMsg, sizeof(WSASendMsg),
137                  &b, NULL, NULL);
138     if(r == SOCKET_ERROR) {
139         errno = wsa_errno();
140         return -1;
141     }
142     assert(b == sizeof(WSASendMsg));
143     assert(WSASendMsg != NULL);
144
145     // convert msghdr to WSAMSG structure
146     wsamsg.name = msg->msg_name;
147     wsamsg.namelen = msg->msg_namelen;
148     wsamsg.lpBuffers = malloc(msg->msg_iovlen * sizeof(WSABUF));
149     wsamsg.dwBufferCount = msg->msg_iovlen;
150     wsamsg.Control.len = msg->msg_controllen;
151     wsamsg.Control.buf = msg->msg_control;
152     wsamsg.dwFlags = 0; // ignored
153
154     if(wsamsg.lpBuffers == NULL) {
155         // malloc will have set errno
156         return -1;
157     }
158
159     // re-wrap iovecs as WSABUFs
160     for(i=0; i<msg->msg_iovlen; i++) {
161         // size_t vs. u_long
162         if(msg->msg_iov[i].iov_len > ULONG_MAX) {
163             free(wsamsg.lpBuffers);
164             errno = EINVAL;
165             return -1;
166         }
167
168         wsamsg.lpBuffers[i].len = msg->msg_iov[i].iov_len;
169         wsamsg.lpBuffers[i].buf = msg->msg_iov[i].iov_base;
170     }
171
172     r = WSASendMsg(sockfd, &wsamsg, flags, &bytessent, NULL, NULL);
173
174     // the allocated WSABUF wrappers are no longer needed
175     free(wsamsg.lpBuffers);
176
177     if(r == SOCKET_ERROR) {
178         // XXX do we need special handling for ENETRESET, ETIMEDOUT?
179         errno = wsa_errno();
180         return -1;
181     }
182
183     // DWORD has one bit more than ssize_t on 32bit
184     // XXX check for this condition before sending anything
185     if(bytessent > SSIZE_MAX) {
186         errno = EINVAL;
187         return -1;
188     }
189
190     return bytessent;
191 }
192
193 int uname(struct utsname *buf)
194 {
195     SYSTEM_INFO si;
196     const char *arch = "unknown";
197
198     memset(buf, 0, sizeof(struct utsname));
199
200     // operating system
201     strncpy(buf->sysname, "Windows", sizeof(buf->sysname)-1);
202     strncpy(buf->release, "unknown", sizeof(buf->sysname)-1);   // we don't need it
203     strncpy(buf->version, "unknown", sizeof(buf->sysname)-1);   // we don't need it
204
205     // computer (node) name
206     if(GetComputerName(buf->nodename, sizeof(buf->nodename)-1) == 0) {
207         errno = EFAULT;
208         return -1;
209     }
210
211     // hardware type
212     GetSystemInfo(&si);
213     switch(si.wProcessorArchitecture) {
214         case PROCESSOR_ARCHITECTURE_AMD64: arch = "amd64"; break;
215         case PROCESSOR_ARCHITECTURE_ARM:   arch = "arm";   break;
216         case PROCESSOR_ARCHITECTURE_IA64:  arch = "ia64";  break;
217         case PROCESSOR_ARCHITECTURE_INTEL: arch = "x86";   break;
218         default: arch = "unknown";
219     }
220     strncpy(buf->machine, arch, sizeof(buf->machine)-1);
221
222     return 0;
223 }