1 /* Accept a connection on a socket, with specific opening flags.
2    Copyright (C) 2009-2021 Free Software Foundation, Inc.
3 
4    This file is free software: you can redistribute it and/or modify
5    it under the terms of the GNU Lesser General Public License as
6    published by the Free Software Foundation; either version 3 of the
7    License, or (at your option) any later version.
8 
9    This file is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 #include <config.h>
18 
19 /* Specification.  */
20 #include <sys/socket.h>
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include "binary-io.h"
25 #if GNULIB_MSVC_NOTHROW
26 # include "msvc-nothrow.h"
27 #else
28 # include <io.h>
29 #endif
30 
31 #ifndef SOCK_CLOEXEC
32 # define SOCK_CLOEXEC 0
33 #endif
34 #ifndef SOCK_NONBLOCK
35 # define SOCK_NONBLOCK 0
36 #endif
37 
38 int
accept4(int sockfd,struct sockaddr * addr,socklen_t * addrlen,int flags)39 accept4 (int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)
40 {
41   int fd;
42 
43 #if HAVE_DECL_ACCEPT4
44 # undef accept4
45   /* Try the system call first, if it exists.  (We may be running with a glibc
46      that has the function but with an older kernel that lacks it.)  */
47   {
48     /* Cache the information whether the system call really exists.  */
49     static int have_accept4_really; /* 0 = unknown, 1 = yes, -1 = no */
50     if (have_accept4_really >= 0)
51       {
52         int result = accept4 (sockfd, addr, addrlen, flags);
53         if (!(result < 0 && errno == ENOSYS))
54           {
55             have_accept4_really = 1;
56             return result;
57           }
58         have_accept4_really = -1;
59       }
60   }
61 #endif
62 
63   /* Check the supported flags.  */
64   if ((flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK | O_TEXT | O_BINARY)) != 0)
65     {
66       errno = EINVAL;
67       return -1;
68     }
69 
70   fd = accept (sockfd, addr, addrlen);
71   if (fd < 0)
72     return -1;
73 
74 #if SOCK_CLOEXEC
75 # if defined _WIN32 && ! defined __CYGWIN__
76 /* Native Windows API.  */
77   if (flags & SOCK_CLOEXEC)
78     {
79       HANDLE curr_process = GetCurrentProcess ();
80       HANDLE old_handle = (HANDLE) _get_osfhandle (fd);
81       HANDLE new_handle;
82       int nfd;
83 
84       if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
85                             old_handle,             /* SourceHandle */
86                             curr_process,           /* TargetProcessHandle */
87                             (PHANDLE) &new_handle,  /* TargetHandle */
88                             (DWORD) 0,              /* DesiredAccess */
89                             FALSE,                  /* InheritHandle */
90                             DUPLICATE_SAME_ACCESS)) /* Options */
91         {
92           close (fd);
93           errno = EBADF; /* arbitrary */
94           return -1;
95         }
96 
97       /* Closing fd before allocating the new fd ensures that the new fd will
98          have the minimum possible value.  */
99       close (fd);
100       nfd = _open_osfhandle ((intptr_t) new_handle,
101                              O_NOINHERIT | (flags & (O_TEXT | O_BINARY)));
102       if (nfd < 0)
103         {
104           CloseHandle (new_handle);
105           return -1;
106         }
107       return nfd;
108     }
109 # else
110 /* Unix API.  */
111   if (flags & SOCK_CLOEXEC)
112     {
113       int fcntl_flags;
114 
115       if ((fcntl_flags = fcntl (fd, F_GETFD, 0)) < 0
116           || fcntl (fd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1)
117         {
118           int saved_errno = errno;
119           close (fd);
120           errno = saved_errno;
121           return -1;
122         }
123     }
124 # endif
125 #endif
126 
127 #if SOCK_NONBLOCK
128   if (flags & SOCK_NONBLOCK)
129     {
130       int fcntl_flags;
131 
132       if ((fcntl_flags = fcntl (fd, F_GETFL, 0)) < 0
133           || fcntl (fd, F_SETFL, fcntl_flags | O_NONBLOCK) == -1)
134         {
135           int saved_errno = errno;
136           close (fd);
137           errno = saved_errno;
138           return -1;
139         }
140     }
141 #endif
142 
143 #if O_BINARY
144   if (flags & O_BINARY)
145     set_binary_mode (fd, O_BINARY);
146   else if (flags & O_TEXT)
147     set_binary_mode (fd, O_TEXT);
148 #endif
149 
150   return fd;
151 }
152