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_REUSEADDR: 145 if (on != apr_is_option_set(sock, APR_SO_REUSEADDR)) { 146 if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(int)) == -1) { 147 return errno; 148 } 149 apr_set_option(sock, APR_SO_REUSEADDR, on); 150 } 151 break; 152 case APR_SO_SNDBUF: 153#ifdef SO_SNDBUF 154 if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF, (void *)&on, sizeof(int)) == -1) { 155 return errno; 156 } 157#else 158 return APR_ENOTIMPL; 159#endif 160 break; 161 case APR_SO_RCVBUF: 162#ifdef SO_RCVBUF 163 if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVBUF, (void *)&on, sizeof(int)) == -1) { 164 return errno; 165 } 166#else 167 return APR_ENOTIMPL; 168#endif 169 break; 170 case APR_SO_NONBLOCK: 171 if (apr_is_option_set(sock, APR_SO_NONBLOCK) != on) { 172 if (on) { 173 if ((rv = sononblock(sock->socketdes)) != APR_SUCCESS) 174 return rv; 175 } 176 else { 177 if ((rv = soblock(sock->socketdes)) != APR_SUCCESS) 178 return rv; 179 } 180 apr_set_option(sock, APR_SO_NONBLOCK, on); 181 } 182 break; 183 case APR_SO_LINGER: 184#ifdef SO_LINGER 185 if (apr_is_option_set(sock, APR_SO_LINGER) != on) { 186 struct linger li; 187 li.l_onoff = on; 188 li.l_linger = APR_MAX_SECS_TO_LINGER; 189 if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(struct linger)) == -1) { 190 return errno; 191 } 192 apr_set_option(sock, APR_SO_LINGER, on); 193 } 194#else 195 return APR_ENOTIMPL; 196#endif 197 break; 198 case APR_TCP_DEFER_ACCEPT: 199#if defined(TCP_DEFER_ACCEPT) 200 if (apr_is_option_set(sock, APR_TCP_DEFER_ACCEPT) != on) { 201 int optlevel = IPPROTO_TCP; 202 int optname = TCP_DEFER_ACCEPT; 203 204 if (setsockopt(sock->socketdes, optlevel, optname, 205 (void *)&on, sizeof(int)) == -1) { 206 return errno; 207 } 208 apr_set_option(sock, APR_TCP_DEFER_ACCEPT, on); 209 } 210#else 211 return APR_ENOTIMPL; 212#endif 213 break; 214 case APR_TCP_NODELAY: 215#if defined(TCP_NODELAY) 216 if (apr_is_option_set(sock, APR_TCP_NODELAY) != on) { 217 int optlevel = IPPROTO_TCP; 218 int optname = TCP_NODELAY; 219 220#if APR_HAVE_SCTP 221 if (sock->protocol == IPPROTO_SCTP) { 222 optlevel = IPPROTO_SCTP; 223 optname = SCTP_NODELAY; 224 } 225#endif 226 if (setsockopt(sock->socketdes, optlevel, optname, (void *)&on, sizeof(int)) == -1) { 227 return errno; 228 } 229 apr_set_option(sock, APR_TCP_NODELAY, on); 230 } 231#else 232 /* BeOS pre-BONE has TCP_NODELAY set by default. 233 * As it can't be turned off we might as well check if they're asking 234 * for it to be turned on! 235 */ 236#ifdef BEOS 237 if (on == 1) 238 return APR_SUCCESS; 239 else 240#endif 241 return APR_ENOTIMPL; 242#endif 243 break; 244 case APR_TCP_NOPUSH: 245#if APR_TCP_NOPUSH_FLAG 246 /* TCP_NODELAY and TCP_CORK are mutually exclusive on Linux 247 * kernels < 2.6; on newer kernels they can be used together 248 * and TCP_CORK takes preference, which is the desired 249 * behaviour. On older kernels, TCP_NODELAY must be toggled 250 * to "off" whilst TCP_CORK is in effect. */ 251 if (apr_is_option_set(sock, APR_TCP_NOPUSH) != on) { 252#ifndef HAVE_TCP_NODELAY_WITH_CORK 253 int optlevel = IPPROTO_TCP; 254 int optname = TCP_NODELAY; 255 256#if APR_HAVE_SCTP 257 if (sock->protocol == IPPROTO_SCTP) { 258 optlevel = IPPROTO_SCTP; 259 optname = SCTP_NODELAY; 260 } 261#endif 262 /* OK we're going to change some settings here... */ 263 if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1 && on) { 264 /* Now toggle TCP_NODELAY to off, if TCP_CORK is being 265 * turned on: */ 266 int tmpflag = 0; 267 if (setsockopt(sock->socketdes, optlevel, optname, 268 (void*)&tmpflag, sizeof(int)) == -1) { 269 return errno; 270 } 271 apr_set_option(sock, APR_RESET_NODELAY, 1); 272 apr_set_option(sock, APR_TCP_NODELAY, 0); 273 } else if (on) { 274 apr_set_option(sock, APR_RESET_NODELAY, 0); 275 } 276#endif /* HAVE_TCP_NODELAY_WITH_CORK */ 277 278 /* OK, now we can just set the TCP_NOPUSH flag accordingly...*/ 279 if (setsockopt(sock->socketdes, IPPROTO_TCP, APR_TCP_NOPUSH_FLAG, 280 (void*)&on, sizeof(int)) == -1) { 281 return errno; 282 } 283 apr_set_option(sock, APR_TCP_NOPUSH, on); 284#ifndef HAVE_TCP_NODELAY_WITH_CORK 285 if (!on && apr_is_option_set(sock, APR_RESET_NODELAY)) { 286 /* Now, if TCP_CORK was just turned off, turn 287 * TCP_NODELAY back on again if it was earlier toggled 288 * to off: */ 289 int tmpflag = 1; 290 if (setsockopt(sock->socketdes, optlevel, optname, 291 (void*)&tmpflag, sizeof(int)) == -1) { 292 return errno; 293 } 294 apr_set_option(sock, APR_RESET_NODELAY,0); 295 apr_set_option(sock, APR_TCP_NODELAY, 1); 296 } 297#endif /* HAVE_TCP_NODELAY_WITH_CORK */ 298 } 299#else 300 return APR_ENOTIMPL; 301#endif 302 break; 303 case APR_INCOMPLETE_READ: 304 apr_set_option(sock, APR_INCOMPLETE_READ, on); 305 break; 306 case APR_IPV6_V6ONLY: 307#if APR_HAVE_IPV6 && defined(IPV6_V6ONLY) 308 /* we don't know the initial setting of this option, 309 * so don't check sock->options since that optimization 310 * won't work 311 */ 312 if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY, 313 (void *)&on, sizeof(int)) == -1) { 314 return errno; 315 } 316 apr_set_option(sock, APR_IPV6_V6ONLY, on); 317#else 318 return APR_ENOTIMPL; 319#endif 320 break; 321 default: 322 return APR_EINVAL; 323 } 324 325 return APR_SUCCESS; 326} 327 328 329apr_status_t apr_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t) 330{ 331 *t = sock->timeout; 332 return APR_SUCCESS; 333} 334 335 336apr_status_t apr_socket_opt_get(apr_socket_t *sock, 337 apr_int32_t opt, apr_int32_t *on) 338{ 339 switch(opt) { 340 default: 341 *on = apr_is_option_set(sock, opt); 342 } 343 return APR_SUCCESS; 344} 345 346 347apr_status_t apr_socket_atmark(apr_socket_t *sock, int *atmark) 348{ 349#ifndef BEOS_R5 350 int oobmark; 351 352 if (ioctl(sock->socketdes, SIOCATMARK, (void*) &oobmark) < 0) 353 return apr_get_netos_error(); 354 355 *atmark = (oobmark != 0); 356 357 return APR_SUCCESS; 358#else /* BEOS_R5 */ 359 return APR_ENOTIMPL; 360#endif 361} 362 363apr_status_t apr_gethostname(char *buf, apr_int32_t len, apr_pool_t *cont) 364{ 365#ifdef BEOS_R5 366 if (gethostname(buf, len) == 0) { 367#else 368 if (gethostname(buf, len) != 0) { 369#endif 370 buf[0] = '\0'; 371 return errno; 372 } 373 else if (!memchr(buf, '\0', len)) { /* buffer too small */ 374 /* note... most platforms just truncate in this condition 375 * linux+glibc return an error 376 */ 377 buf[0] = '\0'; 378 return APR_ENAMETOOLONG; 379 } 380 return APR_SUCCESS; 381} 382 383#if APR_HAS_SO_ACCEPTFILTER 384apr_status_t apr_socket_accept_filter(apr_socket_t *sock, char *nonconst_name, 385 char *nonconst_args) 386{ 387 /* these should have been const; act like they are */ 388 const char *name = nonconst_name; 389 const char *args = nonconst_args; 390 391 struct accept_filter_arg af; 392 socklen_t optlen = sizeof(af); 393 394 /* FreeBSD returns an error if the filter is already set; ignore 395 * this call if we previously set it to the same value. 396 */ 397 if ((getsockopt(sock->socketdes, SOL_SOCKET, SO_ACCEPTFILTER, 398 &af, &optlen)) == 0) { 399 if (!strcmp(name, af.af_name) && !strcmp(args, af.af_arg)) { 400 return APR_SUCCESS; 401 } 402 } 403 404 /* Uhh, at least in FreeBSD 9 the fields are declared as arrays of 405 * these lengths; did sizeof not work in some ancient release? 406 * 407 * FreeBSD kernel sets the last byte to a '\0'. 408 */ 409 apr_cpystrn(af.af_name, name, 16); 410 apr_cpystrn(af.af_arg, args, 256 - 16); 411 412 if ((setsockopt(sock->socketdes, SOL_SOCKET, SO_ACCEPTFILTER, 413 &af, sizeof(af))) < 0) { 414 return errno; 415 } 416 return APR_SUCCESS; 417} 418#endif 419