1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251875Speter * contributor license agreements.  See the NOTICE file distributed with
3251875Speter * this work for additional information regarding copyright ownership.
4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251875Speter * (the "License"); you may not use this file except in compliance with
6251875Speter * the License.  You may obtain a copy of the License at
7251875Speter *
8251875Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251875Speter *
10251875Speter * Unless required by applicable law or agreed to in writing, software
11251875Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251875Speter * See the License for the specific language governing permissions and
14251875Speter * limitations under the License.
15251875Speter */
16251875Speter
17251875Speter#include "apr_arch_networkio.h"
18251875Speter#include "apr_strings.h"
19251875Speter
20251875Speter
21251875Speterstatic apr_status_t soblock(int sd)
22251875Speter{
23251875Speter/* BeOS uses setsockopt at present for non blocking... */
24251875Speter#ifndef BEOS
25251875Speter    int fd_flags;
26251875Speter
27251875Speter    fd_flags = fcntl(sd, F_GETFL, 0);
28251875Speter#if defined(O_NONBLOCK)
29251875Speter    fd_flags &= ~O_NONBLOCK;
30251875Speter#elif defined(O_NDELAY)
31251875Speter    fd_flags &= ~O_NDELAY;
32251875Speter#elif defined(FNDELAY)
33251875Speter    fd_flags &= ~FNDELAY;
34251875Speter#else
35251875Speter#error Please teach APR how to make sockets blocking on your platform.
36251875Speter#endif
37251875Speter    if (fcntl(sd, F_SETFL, fd_flags) == -1) {
38251875Speter        return errno;
39251875Speter    }
40251875Speter#else
41251875Speter    int on = 0;
42251875Speter    if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
43251875Speter        return errno;
44251875Speter#endif /* BEOS */
45251875Speter    return APR_SUCCESS;
46251875Speter}
47251875Speter
48251875Speterstatic apr_status_t sononblock(int sd)
49251875Speter{
50251875Speter#ifndef BEOS
51251875Speter    int fd_flags;
52251875Speter
53251875Speter    fd_flags = fcntl(sd, F_GETFL, 0);
54251875Speter#if defined(O_NONBLOCK)
55251875Speter    fd_flags |= O_NONBLOCK;
56251875Speter#elif defined(O_NDELAY)
57251875Speter    fd_flags |= O_NDELAY;
58251875Speter#elif defined(FNDELAY)
59251875Speter    fd_flags |= FNDELAY;
60251875Speter#else
61251875Speter#error Please teach APR how to make sockets non-blocking on your platform.
62251875Speter#endif
63251875Speter    if (fcntl(sd, F_SETFL, fd_flags) == -1) {
64251875Speter        return errno;
65251875Speter    }
66251875Speter#else
67251875Speter    int on = 1;
68251875Speter    if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
69251875Speter        return errno;
70251875Speter#endif /* BEOS */
71251875Speter    return APR_SUCCESS;
72251875Speter}
73251875Speter
74251875Speter
75251875Speterapr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
76251875Speter{
77251875Speter    apr_status_t stat;
78251875Speter
79251875Speter    /* If our new timeout is non-negative and our old timeout was
80251875Speter     * negative, then we need to ensure that we are non-blocking.
81251875Speter     * Conversely, if our new timeout is negative and we had
82251875Speter     * non-negative timeout, we must make sure our socket is blocking.
83251875Speter     * We want to avoid calling fcntl more than necessary on the
84251875Speter     * socket.
85251875Speter     */
86251875Speter    if (t >= 0 && sock->timeout < 0) {
87251875Speter        if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 1) {
88251875Speter            if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) {
89251875Speter                return stat;
90251875Speter            }
91251875Speter            apr_set_option(sock, APR_SO_NONBLOCK, 1);
92251875Speter        }
93251875Speter    }
94251875Speter    else if (t < 0 && sock->timeout >= 0) {
95251875Speter        if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 0) {
96251875Speter            if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) {
97251875Speter                return stat;
98251875Speter            }
99251875Speter            apr_set_option(sock, APR_SO_NONBLOCK, 0);
100251875Speter        }
101251875Speter    }
102251875Speter    /* must disable the incomplete read support if we disable
103251875Speter     * a timeout
104251875Speter     */
105251875Speter    if (t <= 0) {
106251875Speter        sock->options &= ~APR_INCOMPLETE_READ;
107251875Speter    }
108251875Speter    sock->timeout = t;
109251875Speter    return APR_SUCCESS;
110251875Speter}
111251875Speter
112251875Speter
113251875Speterapr_status_t apr_socket_opt_set(apr_socket_t *sock,
114251875Speter                                apr_int32_t opt, apr_int32_t on)
115251875Speter{
116251875Speter    int one;
117251875Speter    apr_status_t rv;
118251875Speter
119251875Speter    if (on)
120251875Speter        one = 1;
121251875Speter    else
122251875Speter        one = 0;
123251875Speter    switch(opt) {
124251875Speter    case APR_SO_KEEPALIVE:
125251875Speter#ifdef SO_KEEPALIVE
126251875Speter        if (on != apr_is_option_set(sock, APR_SO_KEEPALIVE)) {
127251875Speter            if (setsockopt(sock->socketdes, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, sizeof(int)) == -1) {
128251875Speter                return errno;
129251875Speter            }
130251875Speter            apr_set_option(sock, APR_SO_KEEPALIVE, on);
131251875Speter        }
132251875Speter#else
133251875Speter        return APR_ENOTIMPL;
134251875Speter#endif
135251875Speter        break;
136251875Speter    case APR_SO_DEBUG:
137251875Speter        if (on != apr_is_option_set(sock, APR_SO_DEBUG)) {
138251875Speter            if (setsockopt(sock->socketdes, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(int)) == -1) {
139251875Speter                return errno;
140251875Speter            }
141251875Speter            apr_set_option(sock, APR_SO_DEBUG, on);
142251875Speter        }
143251875Speter        break;
144269847Speter    case APR_SO_BROADCAST:
145269847Speter#ifdef SO_BROADCAST
146269847Speter        if (on != apr_is_option_set(sock, APR_SO_BROADCAST)) {
147269847Speter            if (setsockopt(sock->socketdes, SOL_SOCKET, SO_BROADCAST, (void *)&one, sizeof(int)) == -1) {
148269847Speter                return errno;
149269847Speter            }
150269847Speter            apr_set_option(sock, APR_SO_BROADCAST, on);
151269847Speter        }
152269847Speter#else
153269847Speter        return APR_ENOTIMPL;
154269847Speter#endif
155269847Speter        break;
156251875Speter    case APR_SO_REUSEADDR:
157251875Speter        if (on != apr_is_option_set(sock, APR_SO_REUSEADDR)) {
158251875Speter            if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(int)) == -1) {
159251875Speter                return errno;
160251875Speter            }
161251875Speter            apr_set_option(sock, APR_SO_REUSEADDR, on);
162251875Speter        }
163251875Speter        break;
164251875Speter    case APR_SO_SNDBUF:
165251875Speter#ifdef SO_SNDBUF
166251875Speter        if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF, (void *)&on, sizeof(int)) == -1) {
167251875Speter            return errno;
168251875Speter        }
169251875Speter#else
170251875Speter        return APR_ENOTIMPL;
171251875Speter#endif
172251875Speter        break;
173251875Speter    case APR_SO_RCVBUF:
174251875Speter#ifdef SO_RCVBUF
175251875Speter        if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVBUF, (void *)&on, sizeof(int)) == -1) {
176251875Speter            return errno;
177251875Speter        }
178251875Speter#else
179251875Speter        return APR_ENOTIMPL;
180251875Speter#endif
181251875Speter        break;
182251875Speter    case APR_SO_NONBLOCK:
183251875Speter        if (apr_is_option_set(sock, APR_SO_NONBLOCK) != on) {
184251875Speter            if (on) {
185251875Speter                if ((rv = sononblock(sock->socketdes)) != APR_SUCCESS)
186251875Speter                    return rv;
187251875Speter            }
188251875Speter            else {
189251875Speter                if ((rv = soblock(sock->socketdes)) != APR_SUCCESS)
190251875Speter                    return rv;
191251875Speter            }
192251875Speter            apr_set_option(sock, APR_SO_NONBLOCK, on);
193251875Speter        }
194251875Speter        break;
195251875Speter    case APR_SO_LINGER:
196251875Speter#ifdef SO_LINGER
197251875Speter        if (apr_is_option_set(sock, APR_SO_LINGER) != on) {
198251875Speter            struct linger li;
199251875Speter            li.l_onoff = on;
200251875Speter            li.l_linger = APR_MAX_SECS_TO_LINGER;
201251875Speter            if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(struct linger)) == -1) {
202251875Speter                return errno;
203251875Speter            }
204251875Speter            apr_set_option(sock, APR_SO_LINGER, on);
205251875Speter        }
206251875Speter#else
207251875Speter        return APR_ENOTIMPL;
208251875Speter#endif
209251875Speter        break;
210251875Speter    case APR_TCP_DEFER_ACCEPT:
211251875Speter#if defined(TCP_DEFER_ACCEPT)
212251875Speter        if (apr_is_option_set(sock, APR_TCP_DEFER_ACCEPT) != on) {
213251875Speter            int optlevel = IPPROTO_TCP;
214251875Speter            int optname = TCP_DEFER_ACCEPT;
215251875Speter
216251875Speter            if (setsockopt(sock->socketdes, optlevel, optname,
217251875Speter                           (void *)&on, sizeof(int)) == -1) {
218251875Speter                return errno;
219251875Speter            }
220251875Speter            apr_set_option(sock, APR_TCP_DEFER_ACCEPT, on);
221251875Speter        }
222251875Speter#else
223251875Speter        return APR_ENOTIMPL;
224251875Speter#endif
225251875Speter        break;
226251875Speter    case APR_TCP_NODELAY:
227251875Speter#if defined(TCP_NODELAY)
228251875Speter        if (apr_is_option_set(sock, APR_TCP_NODELAY) != on) {
229251875Speter            int optlevel = IPPROTO_TCP;
230251875Speter            int optname = TCP_NODELAY;
231251875Speter
232251875Speter#if APR_HAVE_SCTP
233251875Speter            if (sock->protocol == IPPROTO_SCTP) {
234251875Speter                optlevel = IPPROTO_SCTP;
235251875Speter                optname = SCTP_NODELAY;
236251875Speter            }
237251875Speter#endif
238251875Speter            if (setsockopt(sock->socketdes, optlevel, optname, (void *)&on, sizeof(int)) == -1) {
239251875Speter                return errno;
240251875Speter            }
241251875Speter            apr_set_option(sock, APR_TCP_NODELAY, on);
242251875Speter        }
243251875Speter#else
244251875Speter        /* BeOS pre-BONE has TCP_NODELAY set by default.
245251875Speter         * As it can't be turned off we might as well check if they're asking
246251875Speter         * for it to be turned on!
247251875Speter         */
248251875Speter#ifdef BEOS
249251875Speter        if (on == 1)
250251875Speter            return APR_SUCCESS;
251251875Speter        else
252251875Speter#endif
253251875Speter        return APR_ENOTIMPL;
254251875Speter#endif
255251875Speter        break;
256251875Speter    case APR_TCP_NOPUSH:
257251875Speter#if APR_TCP_NOPUSH_FLAG
258251875Speter        /* TCP_NODELAY and TCP_CORK are mutually exclusive on Linux
259251875Speter         * kernels < 2.6; on newer kernels they can be used together
260251875Speter         * and TCP_CORK takes preference, which is the desired
261251875Speter         * behaviour.  On older kernels, TCP_NODELAY must be toggled
262251875Speter         * to "off" whilst TCP_CORK is in effect. */
263251875Speter        if (apr_is_option_set(sock, APR_TCP_NOPUSH) != on) {
264251875Speter#ifndef HAVE_TCP_NODELAY_WITH_CORK
265251875Speter            int optlevel = IPPROTO_TCP;
266251875Speter            int optname = TCP_NODELAY;
267251875Speter
268251875Speter#if APR_HAVE_SCTP
269251875Speter            if (sock->protocol == IPPROTO_SCTP) {
270251875Speter                optlevel = IPPROTO_SCTP;
271251875Speter                optname = SCTP_NODELAY;
272251875Speter            }
273251875Speter#endif
274251875Speter            /* OK we're going to change some settings here... */
275251875Speter            if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1 && on) {
276251875Speter                /* Now toggle TCP_NODELAY to off, if TCP_CORK is being
277251875Speter                 * turned on: */
278251875Speter                int tmpflag = 0;
279251875Speter                if (setsockopt(sock->socketdes, optlevel, optname,
280251875Speter                               (void*)&tmpflag, sizeof(int)) == -1) {
281251875Speter                    return errno;
282251875Speter                }
283251875Speter                apr_set_option(sock, APR_RESET_NODELAY, 1);
284251875Speter                apr_set_option(sock, APR_TCP_NODELAY, 0);
285251875Speter            } else if (on) {
286251875Speter                apr_set_option(sock, APR_RESET_NODELAY, 0);
287251875Speter            }
288251875Speter#endif /* HAVE_TCP_NODELAY_WITH_CORK */
289251875Speter
290251875Speter            /* OK, now we can just set the TCP_NOPUSH flag accordingly...*/
291251875Speter            if (setsockopt(sock->socketdes, IPPROTO_TCP, APR_TCP_NOPUSH_FLAG,
292251875Speter                           (void*)&on, sizeof(int)) == -1) {
293251875Speter                return errno;
294251875Speter            }
295251875Speter            apr_set_option(sock, APR_TCP_NOPUSH, on);
296251875Speter#ifndef HAVE_TCP_NODELAY_WITH_CORK
297251875Speter            if (!on && apr_is_option_set(sock, APR_RESET_NODELAY)) {
298251875Speter                /* Now, if TCP_CORK was just turned off, turn
299251875Speter                 * TCP_NODELAY back on again if it was earlier toggled
300251875Speter                 * to off: */
301251875Speter                int tmpflag = 1;
302251875Speter                if (setsockopt(sock->socketdes, optlevel, optname,
303251875Speter                               (void*)&tmpflag, sizeof(int)) == -1) {
304251875Speter                    return errno;
305251875Speter                }
306251875Speter                apr_set_option(sock, APR_RESET_NODELAY,0);
307251875Speter                apr_set_option(sock, APR_TCP_NODELAY, 1);
308251875Speter            }
309251875Speter#endif /* HAVE_TCP_NODELAY_WITH_CORK */
310251875Speter        }
311251875Speter#else
312251875Speter        return APR_ENOTIMPL;
313251875Speter#endif
314251875Speter        break;
315251875Speter    case APR_INCOMPLETE_READ:
316251875Speter        apr_set_option(sock, APR_INCOMPLETE_READ, on);
317251875Speter        break;
318251875Speter    case APR_IPV6_V6ONLY:
319251875Speter#if APR_HAVE_IPV6 && defined(IPV6_V6ONLY)
320251875Speter        /* we don't know the initial setting of this option,
321251875Speter         * so don't check sock->options since that optimization
322251875Speter         * won't work
323251875Speter         */
324251875Speter        if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY,
325251875Speter                       (void *)&on, sizeof(int)) == -1) {
326251875Speter            return errno;
327251875Speter        }
328251875Speter        apr_set_option(sock, APR_IPV6_V6ONLY, on);
329251875Speter#else
330251875Speter        return APR_ENOTIMPL;
331251875Speter#endif
332251875Speter        break;
333251875Speter    default:
334251875Speter        return APR_EINVAL;
335251875Speter    }
336251875Speter
337251875Speter    return APR_SUCCESS;
338251875Speter}
339251875Speter
340251875Speter
341251875Speterapr_status_t apr_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
342251875Speter{
343251875Speter    *t = sock->timeout;
344251875Speter    return APR_SUCCESS;
345251875Speter}
346251875Speter
347251875Speter
348251875Speterapr_status_t apr_socket_opt_get(apr_socket_t *sock,
349251875Speter                                apr_int32_t opt, apr_int32_t *on)
350251875Speter{
351251875Speter    switch(opt) {
352251875Speter        default:
353251875Speter            *on = apr_is_option_set(sock, opt);
354251875Speter    }
355251875Speter    return APR_SUCCESS;
356251875Speter}
357251875Speter
358251875Speter
359251875Speterapr_status_t apr_socket_atmark(apr_socket_t *sock, int *atmark)
360251875Speter{
361251875Speter#ifndef BEOS_R5
362251875Speter    int oobmark;
363251875Speter
364251875Speter    if (ioctl(sock->socketdes, SIOCATMARK, (void*) &oobmark) < 0)
365251875Speter        return apr_get_netos_error();
366251875Speter
367251875Speter    *atmark = (oobmark != 0);
368251875Speter
369251875Speter    return APR_SUCCESS;
370251875Speter#else /* BEOS_R5 */
371251875Speter    return APR_ENOTIMPL;
372251875Speter#endif
373251875Speter}
374251875Speter
375251875Speterapr_status_t apr_gethostname(char *buf, apr_int32_t len, apr_pool_t *cont)
376251875Speter{
377251875Speter#ifdef BEOS_R5
378251875Speter    if (gethostname(buf, len) == 0) {
379251875Speter#else
380251875Speter    if (gethostname(buf, len) != 0) {
381251875Speter#endif
382251875Speter        buf[0] = '\0';
383251875Speter        return errno;
384251875Speter    }
385251875Speter    else if (!memchr(buf, '\0', len)) { /* buffer too small */
386251875Speter        /* note... most platforms just truncate in this condition
387251875Speter         *         linux+glibc return an error
388251875Speter         */
389251875Speter        buf[0] = '\0';
390251875Speter        return APR_ENAMETOOLONG;
391251875Speter    }
392251875Speter    return APR_SUCCESS;
393251875Speter}
394251875Speter
395251875Speter#if APR_HAS_SO_ACCEPTFILTER
396253734Speterapr_status_t apr_socket_accept_filter(apr_socket_t *sock, char *nonconst_name,
397253734Speter                                      char *nonconst_args)
398251875Speter{
399253734Speter    /* these should have been const; act like they are */
400253734Speter    const char *name = nonconst_name;
401253734Speter    const char *args = nonconst_args;
402253734Speter
403251875Speter    struct accept_filter_arg af;
404253734Speter    socklen_t optlen = sizeof(af);
405251875Speter
406253734Speter    /* FreeBSD returns an error if the filter is already set; ignore
407253734Speter     * this call if we previously set it to the same value.
408253734Speter     */
409253734Speter    if ((getsockopt(sock->socketdes, SOL_SOCKET, SO_ACCEPTFILTER,
410253734Speter                    &af, &optlen)) == 0) {
411253734Speter        if (!strcmp(name, af.af_name) && !strcmp(args, af.af_arg)) {
412253734Speter            return APR_SUCCESS;
413253734Speter        }
414253734Speter    }
415253734Speter
416253734Speter    /* Uhh, at least in FreeBSD 9 the fields are declared as arrays of
417253734Speter     * these lengths; did sizeof not work in some ancient release?
418253734Speter     *
419253734Speter     * FreeBSD kernel sets the last byte to a '\0'.
420253734Speter     */
421253734Speter    apr_cpystrn(af.af_name, name, 16);
422253734Speter    apr_cpystrn(af.af_arg, args, 256 - 16);
423253734Speter
424251875Speter    if ((setsockopt(sock->socketdes, SOL_SOCKET, SO_ACCEPTFILTER,
425251875Speter          &af, sizeof(af))) < 0) {
426251875Speter        return errno;
427251875Speter    }
428251875Speter    return APR_SUCCESS;
429251875Speter}
430251875Speter#endif
431