xref: /aosp_15_r20/external/cronet/third_party/apache-portable-runtime/src/network_io/unix/sockopt.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1  /* Licensed to the Apache Software Foundation (ASF) under one or more
2   * contributor license agreements.  See the NOTICE file distributed with
3   * this work for additional information regarding copyright ownership.
4   * The ASF licenses this file to You under the Apache License, Version 2.0
5   * (the "License"); you may not use this file except in compliance with
6   * the License.  You may obtain a copy of the License at
7   *
8   *     http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  #include "apr_arch_networkio.h"
18  #include "apr_strings.h"
19  
20  
soblock(int sd)21  static apr_status_t soblock(int sd)
22  {
23  /* BeOS uses setsockopt at present for non blocking... */
24  #ifndef BEOS
25      int fd_flags;
26  
27      fd_flags = fcntl(sd, F_GETFL, 0);
28  #if defined(O_NONBLOCK)
29      fd_flags &= ~O_NONBLOCK;
30  #elif defined(O_NDELAY)
31      fd_flags &= ~O_NDELAY;
32  #elif defined(FNDELAY)
33      fd_flags &= ~FNDELAY;
34  #else
35  #error Please teach APR how to make sockets blocking on your platform.
36  #endif
37      if (fcntl(sd, F_SETFL, fd_flags) == -1) {
38          return errno;
39      }
40  #else
41      int on = 0;
42      if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
43          return errno;
44  #endif /* BEOS */
45      return APR_SUCCESS;
46  }
47  
sononblock(int sd)48  static apr_status_t sononblock(int sd)
49  {
50  #ifndef BEOS
51      int fd_flags;
52  
53      fd_flags = fcntl(sd, F_GETFL, 0);
54  #if defined(O_NONBLOCK)
55      fd_flags |= O_NONBLOCK;
56  #elif defined(O_NDELAY)
57      fd_flags |= O_NDELAY;
58  #elif defined(FNDELAY)
59      fd_flags |= FNDELAY;
60  #else
61  #error Please teach APR how to make sockets non-blocking on your platform.
62  #endif
63      if (fcntl(sd, F_SETFL, fd_flags) == -1) {
64          return errno;
65      }
66  #else
67      int on = 1;
68      if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
69          return errno;
70  #endif /* BEOS */
71      return APR_SUCCESS;
72  }
73  
74  
apr_socket_timeout_set(apr_socket_t * sock,apr_interval_time_t t)75  apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
76  {
77      apr_status_t stat;
78  
79      /* If our new timeout is non-negative and our old timeout was
80       * negative, then we need to ensure that we are non-blocking.
81       * Conversely, if our new timeout is negative and we had
82       * non-negative timeout, we must make sure our socket is blocking.
83       * We want to avoid calling fcntl more than necessary on the
84       * socket.
85       */
86      if (t >= 0 && sock->timeout < 0) {
87          if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 1) {
88              if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) {
89                  return stat;
90              }
91              apr_set_option(sock, APR_SO_NONBLOCK, 1);
92          }
93      }
94      else if (t < 0 && sock->timeout >= 0) {
95          if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 0) {
96              if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) {
97                  return stat;
98              }
99              apr_set_option(sock, APR_SO_NONBLOCK, 0);
100          }
101      }
102      /* must disable the incomplete read support if we disable
103       * a timeout
104       */
105      if (t <= 0) {
106          sock->options &= ~APR_INCOMPLETE_READ;
107      }
108      sock->timeout = t;
109      return APR_SUCCESS;
110  }
111  
112  
apr_socket_opt_set(apr_socket_t * sock,apr_int32_t opt,apr_int32_t on)113  apr_status_t apr_socket_opt_set(apr_socket_t *sock,
114                                  apr_int32_t opt, apr_int32_t on)
115  {
116      int one;
117      apr_status_t rv;
118  
119      if (on)
120          one = 1;
121      else
122          one = 0;
123      switch(opt) {
124      case APR_SO_KEEPALIVE:
125  #ifdef SO_KEEPALIVE
126          if (on != apr_is_option_set(sock, APR_SO_KEEPALIVE)) {
127              if (setsockopt(sock->socketdes, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, sizeof(int)) == -1) {
128                  return errno;
129              }
130              apr_set_option(sock, APR_SO_KEEPALIVE, on);
131          }
132  #else
133          return APR_ENOTIMPL;
134  #endif
135          break;
136      case APR_SO_DEBUG:
137          if (on != apr_is_option_set(sock, APR_SO_DEBUG)) {
138              if (setsockopt(sock->socketdes, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(int)) == -1) {
139                  return errno;
140              }
141              apr_set_option(sock, APR_SO_DEBUG, on);
142          }
143          break;
144      case APR_SO_BROADCAST:
145  #ifdef SO_BROADCAST
146          if (on != apr_is_option_set(sock, APR_SO_BROADCAST)) {
147              if (setsockopt(sock->socketdes, SOL_SOCKET, SO_BROADCAST, (void *)&one, sizeof(int)) == -1) {
148                  return errno;
149              }
150              apr_set_option(sock, APR_SO_BROADCAST, on);
151          }
152  #else
153          return APR_ENOTIMPL;
154  #endif
155          break;
156      case APR_SO_REUSEADDR:
157          if (on != apr_is_option_set(sock, APR_SO_REUSEADDR)) {
158              if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(int)) == -1) {
159                  return errno;
160              }
161              apr_set_option(sock, APR_SO_REUSEADDR, on);
162          }
163          break;
164      case APR_SO_SNDBUF:
165  #ifdef SO_SNDBUF
166          if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF, (void *)&on, sizeof(int)) == -1) {
167              return errno;
168          }
169  #else
170          return APR_ENOTIMPL;
171  #endif
172          break;
173      case APR_SO_RCVBUF:
174  #ifdef SO_RCVBUF
175          if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVBUF, (void *)&on, sizeof(int)) == -1) {
176              return errno;
177          }
178  #else
179          return APR_ENOTIMPL;
180  #endif
181          break;
182      case APR_SO_NONBLOCK:
183          if (apr_is_option_set(sock, APR_SO_NONBLOCK) != on) {
184              if (on) {
185                  if ((rv = sononblock(sock->socketdes)) != APR_SUCCESS)
186                      return rv;
187              }
188              else {
189                  if ((rv = soblock(sock->socketdes)) != APR_SUCCESS)
190                      return rv;
191              }
192              apr_set_option(sock, APR_SO_NONBLOCK, on);
193          }
194          break;
195      case APR_SO_LINGER:
196  #ifdef SO_LINGER
197          if (apr_is_option_set(sock, APR_SO_LINGER) != on) {
198              struct linger li;
199              li.l_onoff = on;
200              li.l_linger = APR_MAX_SECS_TO_LINGER;
201              if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(struct linger)) == -1) {
202                  return errno;
203              }
204              apr_set_option(sock, APR_SO_LINGER, on);
205          }
206  #else
207          return APR_ENOTIMPL;
208  #endif
209          break;
210      case APR_TCP_DEFER_ACCEPT:
211  #if defined(TCP_DEFER_ACCEPT)
212          if (apr_is_option_set(sock, APR_TCP_DEFER_ACCEPT) != on) {
213              int optlevel = IPPROTO_TCP;
214              int optname = TCP_DEFER_ACCEPT;
215  
216              if (setsockopt(sock->socketdes, optlevel, optname,
217                             (void *)&on, sizeof(int)) == -1) {
218                  return errno;
219              }
220              apr_set_option(sock, APR_TCP_DEFER_ACCEPT, on);
221          }
222  #else
223          return APR_ENOTIMPL;
224  #endif
225          break;
226      case APR_TCP_NODELAY:
227  #if defined(TCP_NODELAY)
228          if (apr_is_option_set(sock, APR_TCP_NODELAY) != on) {
229              int optlevel = IPPROTO_TCP;
230              int optname = TCP_NODELAY;
231  
232  #if APR_HAVE_SCTP
233              if (sock->protocol == IPPROTO_SCTP) {
234                  optlevel = IPPROTO_SCTP;
235                  optname = SCTP_NODELAY;
236              }
237  #endif
238              if (setsockopt(sock->socketdes, optlevel, optname, (void *)&on, sizeof(int)) == -1) {
239                  return errno;
240              }
241              apr_set_option(sock, APR_TCP_NODELAY, on);
242          }
243  #else
244          /* BeOS pre-BONE has TCP_NODELAY set by default.
245           * As it can't be turned off we might as well check if they're asking
246           * for it to be turned on!
247           */
248  #ifdef BEOS
249          if (on == 1)
250              return APR_SUCCESS;
251          else
252  #endif
253          return APR_ENOTIMPL;
254  #endif
255          break;
256      case APR_TCP_NOPUSH:
257  #if APR_TCP_NOPUSH_FLAG
258          /* TCP_NODELAY and TCP_CORK are mutually exclusive on Linux
259           * kernels < 2.6; on newer kernels they can be used together
260           * and TCP_CORK takes preference, which is the desired
261           * behaviour.  On older kernels, TCP_NODELAY must be toggled
262           * to "off" whilst TCP_CORK is in effect. */
263          if (apr_is_option_set(sock, APR_TCP_NOPUSH) != on) {
264  #ifndef HAVE_TCP_NODELAY_WITH_CORK
265              int optlevel = IPPROTO_TCP;
266              int optname = TCP_NODELAY;
267  
268  #if APR_HAVE_SCTP
269              if (sock->protocol == IPPROTO_SCTP) {
270                  optlevel = IPPROTO_SCTP;
271                  optname = SCTP_NODELAY;
272              }
273  #endif
274              /* OK we're going to change some settings here... */
275              if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1 && on) {
276                  /* Now toggle TCP_NODELAY to off, if TCP_CORK is being
277                   * turned on: */
278                  int tmpflag = 0;
279                  if (setsockopt(sock->socketdes, optlevel, optname,
280                                 (void*)&tmpflag, sizeof(int)) == -1) {
281                      return errno;
282                  }
283                  apr_set_option(sock, APR_RESET_NODELAY, 1);
284                  apr_set_option(sock, APR_TCP_NODELAY, 0);
285              } else if (on) {
286                  apr_set_option(sock, APR_RESET_NODELAY, 0);
287              }
288  #endif /* HAVE_TCP_NODELAY_WITH_CORK */
289  
290              /* OK, now we can just set the TCP_NOPUSH flag accordingly...*/
291              if (setsockopt(sock->socketdes, IPPROTO_TCP, APR_TCP_NOPUSH_FLAG,
292                             (void*)&on, sizeof(int)) == -1) {
293                  return errno;
294              }
295              apr_set_option(sock, APR_TCP_NOPUSH, on);
296  #ifndef HAVE_TCP_NODELAY_WITH_CORK
297              if (!on && apr_is_option_set(sock, APR_RESET_NODELAY)) {
298                  /* Now, if TCP_CORK was just turned off, turn
299                   * TCP_NODELAY back on again if it was earlier toggled
300                   * to off: */
301                  int tmpflag = 1;
302                  if (setsockopt(sock->socketdes, optlevel, optname,
303                                 (void*)&tmpflag, sizeof(int)) == -1) {
304                      return errno;
305                  }
306                  apr_set_option(sock, APR_RESET_NODELAY,0);
307                  apr_set_option(sock, APR_TCP_NODELAY, 1);
308              }
309  #endif /* HAVE_TCP_NODELAY_WITH_CORK */
310          }
311  #else
312          return APR_ENOTIMPL;
313  #endif
314          break;
315      case APR_INCOMPLETE_READ:
316          apr_set_option(sock, APR_INCOMPLETE_READ, on);
317          break;
318      case APR_IPV6_V6ONLY:
319  #if APR_HAVE_IPV6 && defined(IPV6_V6ONLY)
320          /* we don't know the initial setting of this option,
321           * so don't check sock->options since that optimization
322           * won't work
323           */
324          if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY,
325                         (void *)&on, sizeof(int)) == -1) {
326              return errno;
327          }
328          apr_set_option(sock, APR_IPV6_V6ONLY, on);
329  #else
330          return APR_ENOTIMPL;
331  #endif
332          break;
333      default:
334          return APR_EINVAL;
335      }
336  
337      return APR_SUCCESS;
338  }
339  
340  
apr_socket_timeout_get(apr_socket_t * sock,apr_interval_time_t * t)341  apr_status_t apr_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
342  {
343      *t = sock->timeout;
344      return APR_SUCCESS;
345  }
346  
347  
apr_socket_opt_get(apr_socket_t * sock,apr_int32_t opt,apr_int32_t * on)348  apr_status_t apr_socket_opt_get(apr_socket_t *sock,
349                                  apr_int32_t opt, apr_int32_t *on)
350  {
351      switch(opt) {
352          default:
353              *on = apr_is_option_set(sock, opt);
354      }
355      return APR_SUCCESS;
356  }
357  
358  
apr_socket_atmark(apr_socket_t * sock,int * atmark)359  apr_status_t apr_socket_atmark(apr_socket_t *sock, int *atmark)
360  {
361  #ifndef BEOS_R5
362      int oobmark;
363  
364      if (ioctl(sock->socketdes, SIOCATMARK, (void*) &oobmark) < 0)
365          return apr_get_netos_error();
366  
367      *atmark = (oobmark != 0);
368  
369      return APR_SUCCESS;
370  #else /* BEOS_R5 */
371      return APR_ENOTIMPL;
372  #endif
373  }
374  
apr_gethostname(char * buf,apr_int32_t len,apr_pool_t * cont)375  apr_status_t apr_gethostname(char *buf, apr_int32_t len, apr_pool_t *cont)
376  {
377  #ifdef BEOS_R5
378      if (gethostname(buf, len) == 0) {
379  #else
380      if (gethostname(buf, len) != 0) {
381  #endif
382          buf[0] = '\0';
383          return errno;
384      }
385      else if (!memchr(buf, '\0', len)) { /* buffer too small */
386          /* note... most platforms just truncate in this condition
387           *         linux+glibc return an error
388           */
389          buf[0] = '\0';
390          return APR_ENAMETOOLONG;
391      }
392      return APR_SUCCESS;
393  }
394  
395  #if APR_HAS_SO_ACCEPTFILTER
396  apr_status_t apr_socket_accept_filter(apr_socket_t *sock, char *nonconst_name,
397                                        char *nonconst_args)
398  {
399      /* these should have been const; act like they are */
400      const char *name = nonconst_name;
401      const char *args = nonconst_args;
402  
403      struct accept_filter_arg af;
404      socklen_t optlen = sizeof(af);
405  
406      /* FreeBSD returns an error if the filter is already set; ignore
407       * this call if we previously set it to the same value.
408       */
409      if ((getsockopt(sock->socketdes, SOL_SOCKET, SO_ACCEPTFILTER,
410                      &af, &optlen)) == 0) {
411          if (!strcmp(name, af.af_name) && !strcmp(args, af.af_arg)) {
412              return APR_SUCCESS;
413          }
414      }
415  
416      /* Uhh, at least in FreeBSD 9 the fields are declared as arrays of
417       * these lengths; did sizeof not work in some ancient release?
418       *
419       * FreeBSD kernel sets the last byte to a '\0'.
420       */
421      apr_cpystrn(af.af_name, name, 16);
422      apr_cpystrn(af.af_arg, args, 256 - 16);
423  
424      if ((setsockopt(sock->socketdes, SOL_SOCKET, SO_ACCEPTFILTER,
425            &af, sizeof(af))) < 0) {
426          return errno;
427      }
428      return APR_SUCCESS;
429  }
430  #endif
431