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 21static 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 48static 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 75apr_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 113apr_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 341apr_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 348apr_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 359apr_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 375apr_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 396apr_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