]> git.pond.sub.org Git - empserver/blob - src/lib/w32/posixio.c
(posix_accept, posix_bind, posix_listen, posix_setsockopt,
[empserver] / src / lib / w32 / posixio.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  posixio.c: POSIX IO emulation layer for WIN32
29  * 
30  *  Known contributors to this file:
31  *     Ron Koenderink, 2007
32  */
33
34 /*
35  * POSIX has just one kind of file descriptors, while Windows has (at
36  * least) two: one for sockets and one for files, with separate
37  * functions to operate on them.  To present a more POSIX-like
38  * interface to our application code, we provide a compatibility layer
39  * that maps POSIX file descriptors to sockets and file handles behind
40  * the scenes.  This actual mapping is done by the fdmap.  It doesn't
41  * implement the finer points of POSIX correctly.  In particular, the
42  * actual values of the file descriptors usually differ.
43  */
44
45 #include <config.h>
46
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <io.h>
50 #include <share.h>
51 #include <stdio.h>
52 #include <stdarg.h>
53 #include "sys/socket.h"
54 #include <sys/stat.h>
55 #include <ws2tcpip.h>
56
57 #include "misc.h"
58 #include "sys/uio.h"
59 #include "unistd.h"
60
61 /*
62  * FD_SETSIZE is the size for the maximum number of sockets.
63  * The number of file descriptors is in a variable _nhandle
64  * based on the assertion output.  In order the simplify the
65  * code and skip dynamic allocation, used double the socket size.
66  */
67 #define MAX_FDS (FD_SETSIZE * 2)
68
69 enum fdmap_io_type {
70     FDMAP_IO_NOTUSED = 0,
71     FDMAP_IO_FILE,
72     FDMAP_IO_SOCKET,
73     FDMAP_IO_ANY /* used for searching only (lookup_handle) */
74 };
75
76 struct fdmap {
77     int handle;
78     enum fdmap_io_type type;
79 };
80
81 static struct fdmap fdmap[MAX_FDS] = {
82     {0, FDMAP_IO_FILE},
83     {1, FDMAP_IO_FILE},
84     {2, FDMAP_IO_FILE}
85 };
86 static int nfd = 3;
87
88 /*
89  * Allocate a POSIX equivalent file descriptor.
90  * Note once get_fd() is called either free_fd() or set_fd()
91  * must be called before thread mutex is released as the
92  * allocation/deallocation code is not thread safe.
93  */
94 static int
95 get_fd(void)
96 {
97     int fd;
98
99     for (fd = 0; fd < nfd && fdmap[fd].type != FDMAP_IO_NOTUSED; fd++) ;
100     if (fd == MAX_FDS) {
101         errno = EMFILE;
102         return -1;
103     }
104     if (fd == nfd) {
105         fdmap[fd].type = FDMAP_IO_NOTUSED;
106         nfd++;
107     }
108     return fd;
109 }
110
111 /*
112  * Deallocate a POSIX equivalent file descriptor.
113  */
114 static void
115 free_fd(int fd)
116 {
117     fdmap[fd].type = FDMAP_IO_NOTUSED;
118     for(; fdmap[nfd - 1].type == FDMAP_IO_NOTUSED; nfd--) ;
119 }
120
121 /*
122  * Complete the allocation of the file descriptor.
123  */
124 static void
125 set_fd(int fd, enum fdmap_io_type type, int handle)
126 {
127     int i;
128
129     fdmap[fd].handle = handle;
130     fdmap[fd].type = type;
131
132     /*
133      * Garbage collection for fileno(), currently not
134      * replacing fclose() and fcloseall() so do not know when
135      * a stream is closed.
136      */
137     for (i = 0; i < nfd; i++) {
138         if (i != fd && type == fdmap[i].type && handle == fdmap[i].handle)
139             free_fd(i);
140     }
141 }
142
143 /*
144  * Find the windows handle (file or socket) for file descriptor.
145  * Return windows handle and type of handle.
146  * You can search for a specific type (FDMAP_IO_FILE or FDMAP_IO_SOCKET)
147  * or for both search by using FDMAP_IO_ANY.
148  * FDMAP_IO_NOTUSED is not valid type to search with.
149  */
150 static int
151 lookup_handle(int fd, enum fdmap_io_type d_type, int error,
152         enum fdmap_io_type *type_ptr, int *handle_ptr)
153 {
154
155     if (fd < 0 || fd >= MAX_FDS) {
156         if (error != 0)
157             errno = error;
158         return 0;
159     } else if ((fdmap[fd].type != d_type && d_type != FDMAP_IO_ANY) ||
160         (fdmap[fd].type == FDMAP_IO_NOTUSED && d_type == FDMAP_IO_ANY)) {
161         if (error != 0)
162             errno = error;
163         return 0;
164     }
165     if (type_ptr != NULL)
166         *type_ptr = fdmap[fd].type;
167     if (handle_ptr != NULL)
168         *handle_ptr = fdmap[fd].handle;
169     return 1;
170 }
171
172 /*
173  * Find and return the file descriptor associated with windows handle.
174  * You can search for FDMAP_IO_FILE or FDMAP_IO_SOCKET.
175  * FDMAP_IO_ANY or FDMAP_IO_NOTUSED is not considered valid search
176  * criteria.
177  */
178 static int
179 lookup_fd(int handle, enum fdmap_io_type d_type)
180 {
181     int i;
182
183     for (i = 0; i < nfd; i++)
184         if (fdmap[i].handle == handle && fdmap[i].type == d_type)
185             return i;
186     return -1;
187 }
188
189 /*
190  * Get the window socket handle for POSIX file descriptor.
191  */
192 int
193 posix_fd2socket(int fd)
194 {
195     int handle;
196     enum fdmap_io_type type;
197
198     if (!lookup_handle(fd, FDMAP_IO_SOCKET, WSAENOTSOCK,
199         &type, &handle))
200         return INVALID_SOCKET;
201     return handle;
202 }
203
204 #define SOCKET_FUNCTION(expr)                           \
205     int result;                                         \
206     int handle;                                         \
207                                                         \
208     if (!lookup_handle(fd, FDMAP_IO_SOCKET,             \
209         ENOTSOCK, NULL, &handle))                       \
210         return -1;                                      \
211                                                         \
212     result = (expr);                                    \
213     if (result == SOCKET_ERROR) {                       \
214         errno = WSAGetLastError();                      \
215         return -1;                                      \
216     }                                                   \
217     return result;
218
219 /*
220  * POSIX equivalent for accept().
221  */
222 #undef accept
223 int
224 posix_accept(int fd, struct sockaddr *addr, socklen_t *addrlen)
225 {
226     int new_fd;
227     int handle;
228     SOCKET new_handle;
229
230     if (!lookup_handle(fd, FDMAP_IO_SOCKET, ENOTSOCK, NULL, &handle))
231         return -1;
232
233     new_fd = get_fd();
234     if (new_fd < 0)
235         return -1;
236
237     new_handle = accept(handle, addr, addrlen);
238     if (new_handle == INVALID_SOCKET) {
239         free_fd(new_fd);
240         errno = WSAGetLastError();
241         return -1;
242     }
243     set_fd(new_fd, FDMAP_IO_SOCKET, (int)new_handle);
244     return new_fd;
245 }
246
247 /*
248  * POSIX equivalent for bind().
249  */
250 #undef bind
251 int
252 posix_bind(int fd, const struct sockaddr *name, socklen_t namelen)
253 {
254     SOCKET_FUNCTION(bind(handle, name, namelen))
255 }
256
257 /*
258  * POSIX equivalent for listen().
259  */
260 #undef listen
261 int
262 posix_listen(int fd, int backlog)
263 {
264     SOCKET_FUNCTION(listen(handle, backlog))
265 }
266
267 /*
268  * POSIX equivalent for setsockopt().
269  */
270 #undef setsockopt
271 int
272 posix_setsockopt(int fd, int level, int optname,
273                       const void *optval, socklen_t optlen)
274 {
275     /*
276      * SO_REUSEADDR requests to permit another bind even when the
277      * port is still in state TIME_WAIT.  Windows' SO_REUSEADDR is
278      * broken: it makes bind() succeed no matter what, even if
279      * there's another server running on the same port.  Luckily,
280      * bind() seems to be broken as well: it seems to succeed while
281      * the port is in state TIME_WAIT by default; thus we get the
282      * behavior we want by not setting SO_REUSEADDR.
283      */
284     if (level == SOL_SOCKET && optname == SO_REUSEADDR)
285         return 0;
286     {
287         SOCKET_FUNCTION(setsockopt(handle, level, optname,
288                         optval, optlen))
289     }
290 }
291
292 /*
293  * POSIX equivalent for shutdown().
294  */
295 #undef shutdown
296 int
297 posix_shutdown(int fd, int how)
298 {
299     SOCKET_FUNCTION(shutdown(handle, how))
300 }
301
302 /*
303  * POSIX equivalent for socket().
304  */
305 #undef socket
306 int
307 posix_socket(int domain, int type, int protocol)
308 {
309     SOCKET handle;
310     int new_fd;
311     
312     if ((new_fd = get_fd()) < 0)
313         return -1;
314
315     handle = socket(domain, type, protocol);
316     if (handle == INVALID_SOCKET) {
317         free_fd(new_fd);
318         errno = WSAGetLastError();
319         return -1;
320     }
321     set_fd(new_fd, FDMAP_IO_SOCKET, (int)handle);
322     return new_fd;
323 }
324
325 #ifdef HAVE_GETADDRINFO
326 const char *
327 inet_ntop(int af, const void *src, char *dst, socklen_t len)
328 {
329     struct sockaddr *sa;
330     struct sockaddr_in sin;
331     struct sockaddr_in6 sin6;
332     size_t salen;
333
334     if (af == AF_INET) {
335         memset(&sin, 0, sizeof(sin));
336         sin.sin_family = af;
337         memcpy(&sin.sin_addr, src, sizeof(sin.sin_addr));
338         sa = (struct sockaddr *)&sin;
339         salen = sizeof(sin);
340     } else if (af == AF_INET6) {
341         memset(&sin6, 0, sizeof(sin6));
342         sin6.sin6_family = af;
343         memcpy(&sin6.sin6_addr, src, sizeof(sin6.sin6_addr));
344         sa = (struct sockaddr *)&sin6;
345         salen = sizeof(sin6);
346     } else {
347         errno = EAFNOSUPPORT;
348         return NULL;
349     }
350
351     if (getnameinfo(sa, salen, dst, len, NULL, 0, NI_NUMERICHOST)) {
352         errno = EAFNOSUPPORT;
353         return NULL;
354     }
355
356     return dst;
357 }
358 #endif
359
360 #define FILE_FUNCTION(type, expr)                               \
361     int handle;                                                 \
362                                                                 \
363     if (!lookup_handle(fd, (type), EBADF, NULL, &handle))       \
364         return -1;                                              \
365                                                                 \
366     return (expr);
367
368 /*
369  * POSIX equivalent for close().
370  */
371 int
372 posix_close(int fd)
373 {
374     int result;
375     int handle;
376     enum fdmap_io_type type;
377
378     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
379         return -1;
380
381     free_fd(fd);
382     switch (type) {
383     case FDMAP_IO_SOCKET:
384         result = closesocket(handle);
385         if (result == SOCKET_ERROR) {
386             errno = WSAGetLastError();
387             return -1;
388         }
389         return result;
390     case FDMAP_IO_FILE:
391        return _close(handle);
392     default:
393         CANT_REACH();
394         return -1;
395     }
396 }
397
398 /*
399  * posix_fsync forces file sync with the disk.
400  * In order for the power report report to accurate timestamp,
401  * the _commit() is to force a sync with disk and therefore
402  * an update for file time.
403  */
404 int
405 posix_fsync(int fd)
406 {
407     FILE_FUNCTION(FDMAP_IO_FILE, _commit(handle))
408 }
409
410 /*
411  * POSIX equivalent for fstat().
412  * fstat() is used instead of _fstat(),
413  * otherwise problems with the 32/64 time definitions
414  * in WIN32.
415  */
416 #undef fstat
417 int
418 posix_fstat(int fd, struct stat *buffer)
419 {
420     FILE_FUNCTION(FDMAP_IO_ANY, fstat(handle, buffer))
421 }
422
423 /*
424  * POSIX equivalent for lseek().
425  */
426 off_t
427 posix_lseek(int fd, off_t offset, int origin)
428 {
429     FILE_FUNCTION(FDMAP_IO_FILE, _lseek(handle, offset, origin))
430 }
431
432 /*
433  * POSIX equivalent for open().
434  * Implements file locks when opening files to provide equivalent
435  * F_GETLK/F_SETLK.
436  */
437 int
438 posix_open(const char *fname, int oflag, ...)
439 {
440     va_list ap;
441     int pmode = 0, new_fd;
442     int handle;
443
444     if (oflag & O_CREAT) {
445         va_start(ap, oflag);
446         pmode = va_arg(ap, int);
447         va_end(ap);
448     }
449
450     if ((new_fd = get_fd()) < 0)
451         return -1;
452
453     /*
454      * We don't implement fcntl() for F_SETLK.  Instead, we lock *all*
455      * files we open.  Not ideal, but it works for Empire.
456      */
457     handle = _sopen(fname, oflag,
458         oflag & O_RDONLY ? SH_DENYNO : SH_DENYWR, pmode);
459     if (handle == -1) {
460         free_fd(new_fd);
461         return -1;
462     }
463     set_fd(new_fd, FDMAP_IO_FILE, handle);
464     return new_fd;
465 }
466
467 #define SHARED_FUNCTION(socket_expr, file_expr)                     \
468     int result;                                                     \
469     int handle;                                                     \
470     enum fdmap_io_type type;                                        \
471                                                                     \
472     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))    \
473         return -1;                                                  \
474                                                                     \
475     switch (type) {                                                 \
476     case FDMAP_IO_SOCKET:                                           \
477         result = (socket_expr);                                     \
478         if (result == SOCKET_ERROR) {                               \
479             errno = WSAGetLastError();                              \
480             return -1;                                              \
481         }                                                           \
482         return result;                                              \
483     case FDMAP_IO_FILE:                                             \
484         return (file_expr);                                         \
485     default:                                                        \
486         CANT_REACH();                                               \
487         return -1;                                                  \
488     }
489
490 /*
491  * POSIX equivalent for read().
492  */
493 ssize_t
494 posix_read(int fd, void *buffer, size_t count)
495 {
496     SHARED_FUNCTION(recv(handle, buffer, count, 0),
497         _read(handle, buffer, count))
498 }
499
500 /*
501  * POSIX equivalent for readv
502  * Modelled after the GNU's libc/sysdeps/posix/readv.c
503  */
504 ssize_t
505 readv(int fd, const struct iovec *iov, int iovcnt)
506 {
507     int i;
508     unsigned char *buffer, *buffer_location;
509     size_t total_bytes = 0;
510     int bytes_read;
511     size_t bytes_left;
512
513     for (i = 0; i < iovcnt; i++) {
514         total_bytes += iov[i].iov_len;
515     }
516
517     buffer = malloc(total_bytes);
518     if (buffer == NULL && total_bytes != 0)
519         return -1;
520
521     bytes_read = posix_read(fd, buffer, total_bytes);
522     if (bytes_read <= 0) {
523         free(buffer);
524         return -1;
525     }
526
527     bytes_left = bytes_read;
528     buffer_location = buffer;
529     for (i = 0; i < iovcnt; i++) {
530         size_t copy = MIN(iov[i].iov_len, bytes_left);
531
532         memcpy(iov[i].iov_base, buffer_location, copy);
533
534         buffer_location += copy;
535         bytes_left -= copy;
536         if (bytes_left == 0)
537             break;
538     }
539
540     free(buffer);
541
542     return bytes_read;
543 }
544
545 /*
546  * POSIX equivalent for write().
547  */
548 ssize_t
549 posix_write(int fd, const void *buffer, size_t count)
550 {
551     SHARED_FUNCTION(send(handle, buffer, count, 0),
552         _write(handle, buffer, count))
553 }
554
555 /*
556  * POSIX equivalent for writev
557  * Modelled after the GNU's libc/sysdeps/posix/writev.c
558  */
559 ssize_t
560 writev(int fd, const struct iovec *iov, int iovcnt)
561 {
562     int i;
563     unsigned char *buffer, *buffer_location;
564     size_t total_bytes = 0;
565     int bytes_written;
566
567     for (i = 0; i < iovcnt; i++)
568         total_bytes += iov[i].iov_len;
569
570     buffer = malloc(total_bytes);
571     if (buffer == NULL && total_bytes != 0)
572         return -1;
573
574     buffer_location = buffer;
575     for (i = 0; i < iovcnt; i++) {
576         memcpy(buffer_location, iov[i].iov_base, iov[i].iov_len);
577         buffer_location += iov[i].iov_len;
578     }
579
580     bytes_written = posix_write(fd, buffer, total_bytes);
581
582     free(buffer);
583
584     if (bytes_written <= 0)
585         return -1;
586     return bytes_written;
587 }
588
589 /*
590  * POSIX equivalent for fileno().
591  * As fopen/fclose/fcloseall are not implemented as POSIX
592  * equivalent functions, the mapping is done when required
593  * by a call to fileno().  The garbage collection of the
594  * file descriptors allocated is done in set_fd() when the
595  * handle is reused.
596  */
597 int
598 fileno(FILE *stream)
599 {
600     int fd;
601     int handle;
602
603     if (stream == NULL) {
604         errno = EBADF;
605         return -1;
606     }
607
608     handle = _fileno(stream);
609
610     fd = lookup_fd(handle, FDMAP_IO_FILE);
611     if (fd >= 0)
612         return fd;
613
614     if ((fd = get_fd()) < 0) {
615         errno = EBADF;
616         return -1;
617     }
618
619     set_fd(fd, FDMAP_IO_FILE, handle);
620     return fd;
621 }
622
623 /*
624  * POSIX equivalent for fcntl().
625  * Currently supports only the F_GETFL/F_SETFL/O_NONBLOCK
626  * Currently ignores F_GETLK/F_SETLK as the file locks are
627  * implement in open()
628  */
629 int
630 fcntl(int fd, int cmd, ...)
631 {
632     va_list ap;
633     int value;
634     unsigned int nonblocking;
635     int result;
636     long bytes_returned;
637     int handle;
638     enum fdmap_io_type type;
639
640     if (!lookup_handle(fd, FDMAP_IO_ANY, EBADF, &type, &handle))
641         return -1;
642
643     switch (cmd)
644     {
645     case F_GETFL:
646         /*
647          * F_GETFL and F_SETFL only support O_NONBLOCK
648          * for sockets currently
649          */
650         if (type == FDMAP_IO_SOCKET) {
651             result = WSAIoctl(handle, FIONBIO, NULL, 0,&nonblocking,
652                 sizeof (nonblocking), &bytes_returned, NULL, NULL);
653         
654             if(result < 0) {
655                 errno = WSAGetLastError();
656                 return -1;
657             }
658
659             if (nonblocking)
660                 return O_NONBLOCK;
661             else
662                 return 0;
663         }
664         break;
665     case F_SETFL:
666         if (type == FDMAP_IO_SOCKET) {
667             va_start(ap, cmd);
668             value = va_arg(ap, int);
669             va_end(ap);
670             if (value & O_NONBLOCK)
671                 nonblocking = 1;
672             else
673                 nonblocking = 0;
674
675             result = WSAIoctl(handle, FIONBIO, &nonblocking,
676                 sizeof (nonblocking), NULL, 0, &bytes_returned,
677                 NULL, NULL);
678
679             if(result < 0) {
680                 errno = WSAGetLastError();
681                 return -1;
682             }
683             return result;
684         }
685         break;
686     case F_SETLK:
687         /*
688          * The POSIX equivalent is not available in WIN32
689          * That implement the file locking in the file open
690          * by using sopen instead of open.
691          */
692         return 0;
693     }
694     errno = EINVAL;
695     return -1;
696 }