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