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_private.h"
18#if BEOS_BONE /* BONE uses the unix code - woohoo */
19#include "../unix/sendrecv.c"
20#else
21#include "apr_arch_networkio.h"
22#include "apr_time.h"
23
24static apr_status_t wait_for_io_or_timeout(apr_socket_t *sock, int for_read)
25{
26    struct timeval tv, *tvptr;
27    fd_set fdset;
28    int srv;
29
30    do {
31        FD_ZERO(&fdset);
32        FD_SET(sock->socketdes, &fdset);
33        if (sock->timeout < 0) {
34            tvptr = NULL;
35        }
36        else {
37            tv.tv_sec = sock->timeout / APR_USEC_PER_SEC;
38            tv.tv_usec = sock->timeout % APR_USEC_PER_SEC;
39            tvptr = &tv;
40        }
41        srv = select(sock->socketdes + 1,
42            for_read ? &fdset : NULL,
43            for_read ? NULL : &fdset,
44            NULL,
45            tvptr);
46            /* TODO - timeout should be smaller on repeats of this loop */
47    } while (srv == -1 && errno == EINTR);
48
49    if (srv == 0) {
50        return APR_TIMEUP;
51    }
52    else if (srv < 0) {
53        return errno;
54    }
55    return APR_SUCCESS;
56}
57
58#define SEND_WAIT APR_USEC_PER_SEC / 10
59
60APR_DECLARE(apr_status_t) apr_socket_send(apr_socket_t *sock, const char *buf,
61                                          apr_size_t *len)
62{
63    apr_ssize_t rv;
64
65    do {
66        rv = send(sock->socketdes, buf, (*len), 0);
67    } while (rv == -1 && errno == EINTR);
68
69    if (rv == -1 && errno == EWOULDBLOCK && sock->timeout > 0) {
70        apr_int32_t snooze_val = SEND_WAIT;
71        apr_int32_t zzz = 0;
72
73        do {
74            rv = send(sock->socketdes, buf, (*len), 0);
75            if (rv == -1 && errno == EWOULDBLOCK){
76                apr_sleep (snooze_val);
77                zzz += snooze_val;
78                snooze_val += SEND_WAIT;
79                /* have we passed our timeout value */
80                if (zzz > (sock->timeout * APR_USEC_PER_SEC))
81                    break;
82            }
83        } while (rv == -1 && (errno == EINTR || errno == EWOULDBLOCK));
84    }
85    if (rv == -1) {
86        *len = 0;
87        return errno;
88    }
89    (*len) = rv;
90
91    return APR_SUCCESS;
92}
93
94APR_DECLARE(apr_status_t) apr_socket_recv(apr_socket_t *sock, char *buf,
95                                          apr_size_t *len)
96{
97    apr_ssize_t rv;
98
99    do {
100        rv = recv(sock->socketdes, buf, (*len), 0);
101    } while (rv == -1 && errno == EINTR);
102
103    if (rv == -1 && errno == EWOULDBLOCK && sock->timeout > 0) {
104        apr_status_t arv = wait_for_io_or_timeout(sock, 1);
105        if (arv != APR_SUCCESS) {
106            *len = 0;
107            return arv;
108        }
109        else {
110            do {
111                rv = recv(sock->socketdes, buf, (*len), 0);
112            } while (rv == -1 && errno == EINTR);
113        }
114    }
115    if (rv == -1) {
116        (*len) = 0;
117        return errno;
118    }
119    (*len) = rv;
120    if (rv == 0)
121        return APR_EOF;
122    return APR_SUCCESS;
123}
124
125/* BeOS doesn't have writev for sockets so we use the following instead...
126 */
127APR_DECLARE(apr_status_t) apr_socket_sendv(apr_socket_t * sock,
128                                           const struct iovec *vec,
129                                           apr_int32_t nvec, apr_size_t *len)
130{
131    *len = vec[0].iov_len;
132    return apr_socket_send(sock, vec[0].iov_base, len);
133}
134
135APR_DECLARE(apr_status_t) apr_socket_sendto(apr_socket_t *sock,
136                                            apr_sockaddr_t *where,
137                                            apr_int32_t flags, const char *buf,
138                                            apr_size_t *len)
139{
140    apr_ssize_t rv;
141
142    do {
143        rv = sendto(sock->socketdes, buf, (*len), flags,
144                    (const struct sockaddr*)&where->sa,
145                    where->salen);
146    } while (rv == -1 && errno == EINTR);
147
148    if (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)
149        && sock->timeout != 0) {
150        apr_status_t arv = wait_for_io_or_timeout(sock, 0);
151        if (arv != APR_SUCCESS) {
152            *len = 0;
153            return arv;
154        } else {
155            do {
156                rv = sendto(sock->socketdes, buf, (*len), flags,
157                            (const struct sockaddr*)&where->sa,
158                            where->salen);
159            } while (rv == -1 && errno == EINTR);
160        }
161    }
162    if (rv == -1) {
163        *len = 0;
164        return errno;
165    }
166    *len = rv;
167    return APR_SUCCESS;
168}
169
170APR_DECLARE(apr_status_t) apr_socket_recvfrom(apr_sockaddr_t *from,
171                                              apr_socket_t *sock,
172                                              apr_int32_t flags, char *buf,
173                                              apr_size_t *len)
174{
175    apr_ssize_t rv;
176
177    if (from == NULL){
178        return APR_ENOMEM;
179        /* Not sure if this is correct.  Maybe we should just allocate
180           the memory??
181         */
182    }
183
184    do {
185        rv = recvfrom(sock->socketdes, buf, (*len), flags,
186                      (struct sockaddr*)&from->sa, &from->salen);
187    } while (rv == -1 && errno == EINTR);
188
189    if (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) &&
190        sock->timeout != 0) {
191        apr_status_t arv = wait_for_io_or_timeout(sock, 1);
192        if (arv != APR_SUCCESS) {
193            *len = 0;
194            return arv;
195        } else {
196            do {
197                rv = recvfrom(sock->socketdes, buf, (*len), flags,
198                              (struct sockaddr*)&from->sa, &from->salen);
199                } while (rv == -1 && errno == EINTR);
200        }
201    }
202    if (rv == -1) {
203        (*len) = 0;
204        return errno;
205    }
206
207    from->port = ntohs(from->sa.sin.sin_port);
208
209    (*len) = rv;
210    if (rv == 0)
211        return APR_EOF;
212
213    return APR_SUCCESS;
214}
215
216#endif /* ! BEOS_BONE */
217