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