summaryrefslogtreecommitdiffstats
path: root/network_io
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:23:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:23:09 +0000
commit30d479c28c831a0d4f1fdb54a9e346b0fc176be1 (patch)
treeaa35d7414ce9f1326abf6f723f6dfa5b0aa08b1d /network_io
parentInitial commit. (diff)
downloadapr-upstream/1.7.2.tar.xz
apr-upstream/1.7.2.zip
Adding upstream version 1.7.2.upstream/1.7.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'network_io')
-rw-r--r--network_io/beos/sendrecv.c216
-rw-r--r--network_io/beos/socketcommon.c6
-rw-r--r--network_io/os2/inet_ntop.c1
-rw-r--r--network_io/os2/inet_pton.c1
-rw-r--r--network_io/os2/os2calls.c132
-rw-r--r--network_io/os2/sendrecv.c155
-rw-r--r--network_io/os2/sendrecv_udp.c104
-rw-r--r--network_io/os2/sockaddr.c1
-rw-r--r--network_io/os2/socket_util.c1
-rw-r--r--network_io/os2/sockets.c317
-rw-r--r--network_io/os2/sockopt.c144
-rw-r--r--network_io/unix/inet_ntop.c243
-rw-r--r--network_io/unix/inet_pton.c240
-rw-r--r--network_io/unix/multicast.c313
-rw-r--r--network_io/unix/sendrecv.c1110
-rw-r--r--network_io/unix/sockaddr.c1293
-rw-r--r--network_io/unix/socket_util.c75
-rw-r--r--network_io/unix/sockets.c572
-rw-r--r--network_io/unix/sockopt.c465
-rw-r--r--network_io/win32/sendrecv.c442
-rw-r--r--network_io/win32/sockets.c559
-rw-r--r--network_io/win32/sockopt.c302
22 files changed, 6692 insertions, 0 deletions
diff --git a/network_io/beos/sendrecv.c b/network_io/beos/sendrecv.c
new file mode 100644
index 0000000..201abf8
--- /dev/null
+++ b/network_io/beos/sendrecv.c
@@ -0,0 +1,216 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_private.h"
+#if BEOS_BONE /* BONE uses the unix code - woohoo */
+#include "../unix/sendrecv.c"
+#else
+#include "apr_arch_networkio.h"
+#include "apr_time.h"
+
+static apr_status_t wait_for_io_or_timeout(apr_socket_t *sock, int for_read)
+{
+ struct timeval tv, *tvptr;
+ fd_set fdset;
+ int srv;
+
+ do {
+ FD_ZERO(&fdset);
+ FD_SET(sock->socketdes, &fdset);
+ if (sock->timeout < 0) {
+ tvptr = NULL;
+ }
+ else {
+ tv.tv_sec = sock->timeout / APR_USEC_PER_SEC;
+ tv.tv_usec = sock->timeout % APR_USEC_PER_SEC;
+ tvptr = &tv;
+ }
+ srv = select(sock->socketdes + 1,
+ for_read ? &fdset : NULL,
+ for_read ? NULL : &fdset,
+ NULL,
+ tvptr);
+ /* TODO - timeout should be smaller on repeats of this loop */
+ } while (srv == -1 && errno == EINTR);
+
+ if (srv == 0) {
+ return APR_TIMEUP;
+ }
+ else if (srv < 0) {
+ return errno;
+ }
+ return APR_SUCCESS;
+}
+
+#define SEND_WAIT APR_USEC_PER_SEC / 10
+
+APR_DECLARE(apr_status_t) apr_socket_send(apr_socket_t *sock, const char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ do {
+ rv = send(sock->socketdes, buf, (*len), 0);
+ } while (rv == -1 && errno == EINTR);
+
+ if (rv == -1 && errno == EWOULDBLOCK && sock->timeout > 0) {
+ apr_int32_t snooze_val = SEND_WAIT;
+ apr_int32_t zzz = 0;
+
+ do {
+ rv = send(sock->socketdes, buf, (*len), 0);
+ if (rv == -1 && errno == EWOULDBLOCK){
+ apr_sleep (snooze_val);
+ zzz += snooze_val;
+ snooze_val += SEND_WAIT;
+ /* have we passed our timeout value */
+ if (zzz > (sock->timeout * APR_USEC_PER_SEC))
+ break;
+ }
+ } while (rv == -1 && (errno == EINTR || errno == EWOULDBLOCK));
+ }
+ if (rv == -1) {
+ *len = 0;
+ return errno;
+ }
+ (*len) = rv;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_recv(apr_socket_t *sock, char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ do {
+ rv = recv(sock->socketdes, buf, (*len), 0);
+ } while (rv == -1 && errno == EINTR);
+
+ if (rv == -1 && errno == EWOULDBLOCK && sock->timeout > 0) {
+ apr_status_t arv = wait_for_io_or_timeout(sock, 1);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ else {
+ do {
+ rv = recv(sock->socketdes, buf, (*len), 0);
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ if (rv == -1) {
+ (*len) = 0;
+ return errno;
+ }
+ (*len) = rv;
+ if (rv == 0)
+ return APR_EOF;
+ return APR_SUCCESS;
+}
+
+/* BeOS doesn't have writev for sockets so we use the following instead...
+ */
+APR_DECLARE(apr_status_t) apr_socket_sendv(apr_socket_t * sock,
+ const struct iovec *vec,
+ apr_int32_t nvec, apr_size_t *len)
+{
+ *len = vec[0].iov_len;
+ return apr_socket_send(sock, vec[0].iov_base, len);
+}
+
+APR_DECLARE(apr_status_t) apr_socket_sendto(apr_socket_t *sock,
+ apr_sockaddr_t *where,
+ apr_int32_t flags, const char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ do {
+ rv = sendto(sock->socketdes, buf, (*len), flags,
+ (const struct sockaddr*)&where->sa,
+ where->salen);
+ } while (rv == -1 && errno == EINTR);
+
+ if (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && sock->timeout != 0) {
+ apr_status_t arv = wait_for_io_or_timeout(sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ } else {
+ do {
+ rv = sendto(sock->socketdes, buf, (*len), flags,
+ (const struct sockaddr*)&where->sa,
+ where->salen);
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ if (rv == -1) {
+ *len = 0;
+ return errno;
+ }
+ *len = rv;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_recvfrom(apr_sockaddr_t *from,
+ apr_socket_t *sock,
+ apr_int32_t flags, char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ if (from == NULL){
+ return APR_ENOMEM;
+ /* Not sure if this is correct. Maybe we should just allocate
+ the memory??
+ */
+ }
+
+ do {
+ rv = recvfrom(sock->socketdes, buf, (*len), flags,
+ (struct sockaddr*)&from->sa, &from->salen);
+ } while (rv == -1 && errno == EINTR);
+
+ if (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) &&
+ sock->timeout != 0) {
+ apr_status_t arv = wait_for_io_or_timeout(sock, 1);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ } else {
+ do {
+ rv = recvfrom(sock->socketdes, buf, (*len), flags,
+ (struct sockaddr*)&from->sa, &from->salen);
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ if (rv == -1) {
+ (*len) = 0;
+ return errno;
+ }
+
+ from->port = ntohs(from->sa.sin.sin_port);
+
+ (*len) = rv;
+ if (rv == 0)
+ return APR_EOF;
+
+ return APR_SUCCESS;
+}
+
+#endif /* ! BEOS_BONE */
diff --git a/network_io/beos/socketcommon.c b/network_io/beos/socketcommon.c
new file mode 100644
index 0000000..b9f594b
--- /dev/null
+++ b/network_io/beos/socketcommon.c
@@ -0,0 +1,6 @@
+#include "../unix/inet_ntop.c"
+#include "../unix/inet_pton.c"
+#include "../unix/sockets.c"
+#include "../unix/sockaddr.c"
+#include "../unix/sockopt.c"
+#include "../unix/socket_util.c"
diff --git a/network_io/os2/inet_ntop.c b/network_io/os2/inet_ntop.c
new file mode 100644
index 0000000..f1f79d4
--- /dev/null
+++ b/network_io/os2/inet_ntop.c
@@ -0,0 +1 @@
+#include "../unix/inet_ntop.c"
diff --git a/network_io/os2/inet_pton.c b/network_io/os2/inet_pton.c
new file mode 100644
index 0000000..dbd3ac4
--- /dev/null
+++ b/network_io/os2/inet_pton.c
@@ -0,0 +1 @@
+#include "../unix/inet_pton.c"
diff --git a/network_io/os2/os2calls.c b/network_io/os2/os2calls.c
new file mode 100644
index 0000000..6bf1fcd
--- /dev/null
+++ b/network_io/os2/os2calls.c
@@ -0,0 +1,132 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_network_io.h"
+#include "apr_portable.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+
+static int os2_socket_init(int, int ,int);
+
+int (*apr_os2_socket)(int, int, int) = os2_socket_init;
+int (*apr_os2_select)(int *, int, int, int, long) = NULL;
+int (*apr_os2_sock_errno)() = NULL;
+int (*apr_os2_accept)(int, struct sockaddr *, int *) = NULL;
+int (*apr_os2_bind)(int, struct sockaddr *, int) = NULL;
+int (*apr_os2_connect)(int, struct sockaddr *, int) = NULL;
+int (*apr_os2_getpeername)(int, struct sockaddr *, int *) = NULL;
+int (*apr_os2_getsockname)(int, struct sockaddr *, int *) = NULL;
+int (*apr_os2_getsockopt)(int, int, int, char *, int *) = NULL;
+int (*apr_os2_ioctl)(int, int, caddr_t, int) = NULL;
+int (*apr_os2_listen)(int, int) = NULL;
+int (*apr_os2_recv)(int, char *, int, int) = NULL;
+int (*apr_os2_send)(int, const char *, int, int) = NULL;
+int (*apr_os2_setsockopt)(int, int, int, char *, int) = NULL;
+int (*apr_os2_shutdown)(int, int) = NULL;
+int (*apr_os2_soclose)(int) = NULL;
+int (*apr_os2_writev)(int, struct iovec *, int) = NULL;
+int (*apr_os2_sendto)(int, const char *, int, int, const struct sockaddr *, int);
+int (*apr_os2_recvfrom)(int, char *, int, int, struct sockaddr *, int *);
+
+static HMODULE hSO32DLL;
+
+static int os2_fn_link()
+{
+ DosEnterCritSec(); /* Stop two threads doing this at the same time */
+
+ if (apr_os2_socket == os2_socket_init) {
+ ULONG rc;
+ char errorstr[200];
+
+ rc = DosLoadModule(errorstr, sizeof(errorstr), "SO32DLL", &hSO32DLL);
+
+ if (rc)
+ return APR_OS2_STATUS(rc);
+
+ rc = DosQueryProcAddr(hSO32DLL, 0, "SOCKET", &apr_os2_socket);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "SELECT", &apr_os2_select);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "SOCK_ERRNO", &apr_os2_sock_errno);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "ACCEPT", &apr_os2_accept);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "BIND", &apr_os2_bind);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "CONNECT", &apr_os2_connect);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "GETPEERNAME", &apr_os2_getpeername);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "GETSOCKNAME", &apr_os2_getsockname);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "GETSOCKOPT", &apr_os2_getsockopt);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "IOCTL", &apr_os2_ioctl);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "LISTEN", &apr_os2_listen);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "RECV", &apr_os2_recv);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "SEND", &apr_os2_send);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "SETSOCKOPT", &apr_os2_setsockopt);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "SHUTDOWN", &apr_os2_shutdown);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "SOCLOSE", &apr_os2_soclose);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "WRITEV", &apr_os2_writev);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "SENDTO", &apr_os2_sendto);
+
+ if (!rc)
+ rc = DosQueryProcAddr(hSO32DLL, 0, "RECVFROM", &apr_os2_recvfrom);
+
+ if (rc)
+ return APR_OS2_STATUS(rc);
+ }
+
+ DosExitCritSec();
+ return APR_SUCCESS;
+}
+
+
+
+static int os2_socket_init(int domain, int type, int protocol)
+{
+ int rc = os2_fn_link();
+ if (rc == APR_SUCCESS)
+ return apr_os2_socket(domain, type, protocol);
+ return rc;
+}
diff --git a/network_io/os2/sendrecv.c b/network_io/os2/sendrecv.c
new file mode 100644
index 0000000..839ff3f
--- /dev/null
+++ b/network_io/os2/sendrecv.c
@@ -0,0 +1,155 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_network_io.h"
+#include "apr_lib.h"
+#include <sys/time.h>
+
+APR_DECLARE(apr_status_t) apr_socket_send(apr_socket_t *sock, const char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+ int fds, err = 0;
+
+ if (*len > 65536) {
+ *len = 65536;
+ }
+
+ do {
+ if (!sock->nonblock || err == SOCEWOULDBLOCK) {
+ fds = sock->socketdes;
+ rv = select(&fds, 0, 1, 0, sock->timeout >= 0 ? sock->timeout/1000 : -1);
+
+ if (rv != 1) {
+ *len = 0;
+ err = sock_errno();
+
+ if (rv == 0)
+ return APR_TIMEUP;
+
+ if (err == SOCEINTR)
+ continue;
+
+ return APR_OS2_STATUS(err);
+ }
+ }
+
+ rv = send(sock->socketdes, buf, (*len), 0);
+ err = rv < 0 ? sock_errno() : 0;
+ } while (err == SOCEINTR || err == SOCEWOULDBLOCK);
+
+ if (err) {
+ *len = 0;
+ return APR_OS2_STATUS(err);
+ }
+
+ (*len) = rv;
+ return APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_socket_recv(apr_socket_t *sock, char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+ int fds, err = 0;
+
+ do {
+ if (!sock->nonblock || (err == SOCEWOULDBLOCK && sock->timeout != 0)) {
+ fds = sock->socketdes;
+ rv = select(&fds, 1, 0, 0, sock->timeout >= 0 ? sock->timeout/1000 : -1);
+
+ if (rv != 1) {
+ *len = 0;
+ err = sock_errno();
+
+ if (rv == 0)
+ return APR_TIMEUP;
+
+ if (err == SOCEINTR)
+ continue;
+
+ return APR_OS2_STATUS(err);
+ }
+ }
+
+ rv = recv(sock->socketdes, buf, (*len), 0);
+ err = rv < 0 ? sock_errno() : 0;
+ } while (err == SOCEINTR || (err == SOCEWOULDBLOCK && sock->timeout != 0));
+
+ if (err) {
+ *len = 0;
+ return APR_OS2_STATUS(err);
+ }
+
+ (*len) = rv;
+ return rv == 0 ? APR_EOF : APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_socket_sendv(apr_socket_t *sock,
+ const struct iovec *vec,
+ apr_int32_t nvec, apr_size_t *len)
+{
+ apr_status_t rv;
+ struct iovec *tmpvec;
+ int fds, err = 0;
+ int nv_tosend, total = 0;
+
+ /* Make sure writev() only gets fed 64k at a time */
+ for ( nv_tosend = 0; nv_tosend < nvec && total + vec[nv_tosend].iov_len < 65536; nv_tosend++ ) {
+ total += vec[nv_tosend].iov_len;
+ }
+
+ tmpvec = alloca(sizeof(struct iovec) * nv_tosend);
+ memcpy(tmpvec, vec, sizeof(struct iovec) * nv_tosend);
+
+ do {
+ if (!sock->nonblock || err == SOCEWOULDBLOCK) {
+ fds = sock->socketdes;
+ rv = select(&fds, 0, 1, 0, sock->timeout >= 0 ? sock->timeout/1000 : -1);
+
+ if (rv != 1) {
+ *len = 0;
+ err = sock_errno();
+
+ if (rv == 0)
+ return APR_TIMEUP;
+
+ if (err == SOCEINTR)
+ continue;
+
+ return APR_OS2_STATUS(err);
+ }
+ }
+
+ rv = writev(sock->socketdes, tmpvec, nv_tosend);
+ err = rv < 0 ? sock_errno() : 0;
+ } while (err == SOCEINTR || err == SOCEWOULDBLOCK);
+
+ if (err) {
+ *len = 0;
+ return APR_OS2_STATUS(err);
+ }
+
+ *len = rv;
+ return APR_SUCCESS;
+}
diff --git a/network_io/os2/sendrecv_udp.c b/network_io/os2/sendrecv_udp.c
new file mode 100644
index 0000000..c0dcd85
--- /dev/null
+++ b/network_io/os2/sendrecv_udp.c
@@ -0,0 +1,104 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_network_io.h"
+#include "apr_support.h"
+#include "apr_lib.h"
+#include <sys/time.h>
+
+
+APR_DECLARE(apr_status_t) apr_socket_sendto(apr_socket_t *sock,
+ apr_sockaddr_t *where,
+ apr_int32_t flags, const char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+ int serrno;
+
+ do {
+ rv = sendto(sock->socketdes, buf, (*len), flags,
+ (struct sockaddr*)&where->sa,
+ where->salen);
+ } while (rv == -1 && (serrno = sock_errno()) == EINTR);
+
+ if (rv == -1 && serrno == SOCEWOULDBLOCK && sock->timeout != 0) {
+ apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ } else {
+ do {
+ rv = sendto(sock->socketdes, buf, *len, flags,
+ (const struct sockaddr*)&where->sa,
+ where->salen);
+ } while (rv == -1 && (serrno = sock_errno()) == SOCEINTR);
+ }
+ }
+
+ if (rv == -1) {
+ *len = 0;
+ return APR_FROM_OS_ERROR(serrno);
+ }
+
+ *len = rv;
+ return APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_socket_recvfrom(apr_sockaddr_t *from,
+ apr_socket_t *sock,
+ apr_int32_t flags, char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+ int serrno;
+
+ do {
+ rv = recvfrom(sock->socketdes, buf, (*len), flags,
+ (struct sockaddr*)&from->sa, &from->salen);
+ } while (rv == -1 && (serrno = sock_errno()) == EINTR);
+
+ if (rv == -1 && serrno == SOCEWOULDBLOCK && sock->timeout != 0) {
+ apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 1);
+
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ } else {
+ do {
+ rv = recvfrom(sock->socketdes, buf, *len, flags,
+ (struct sockaddr*)&from->sa, &from->salen);
+ } while (rv == -1 && (serrno = sock_errno()) == EINTR);
+ }
+ }
+
+ if (rv == -1) {
+ (*len) = 0;
+ return APR_FROM_OS_ERROR(serrno);
+ }
+
+ (*len) = rv;
+
+ if (rv == 0 && sock->type == SOCK_STREAM)
+ return APR_EOF;
+
+ return APR_SUCCESS;
+}
diff --git a/network_io/os2/sockaddr.c b/network_io/os2/sockaddr.c
new file mode 100644
index 0000000..2afe4b7
--- /dev/null
+++ b/network_io/os2/sockaddr.c
@@ -0,0 +1 @@
+#include "../unix/sockaddr.c"
diff --git a/network_io/os2/socket_util.c b/network_io/os2/socket_util.c
new file mode 100644
index 0000000..cdc1cea
--- /dev/null
+++ b/network_io/os2/socket_util.c
@@ -0,0 +1 @@
+#include "../unix/socket_util.c"
diff --git a/network_io/os2/sockets.c b/network_io/os2/sockets.c
new file mode 100644
index 0000000..bb951e4
--- /dev/null
+++ b/network_io/os2/sockets.c
@@ -0,0 +1,317 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_arch_inherit.h"
+#include "apr_network_io.h"
+#include "apr_general.h"
+#include "apr_portable.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "apr_arch_os2calls.h"
+
+static apr_status_t socket_cleanup(void *sock)
+{
+ apr_socket_t *thesocket = sock;
+
+ if (thesocket->socketdes < 0) {
+ return APR_EINVALSOCK;
+ }
+
+ if (soclose(thesocket->socketdes) == 0) {
+ thesocket->socketdes = -1;
+ return APR_SUCCESS;
+ }
+ else {
+ return APR_OS2_STATUS(sock_errno());
+ }
+}
+
+static void set_socket_vars(apr_socket_t *sock, int family, int type, int protocol)
+{
+ sock->type = type;
+ sock->protocol = protocol;
+ apr_sockaddr_vars_set(sock->local_addr, family, 0);
+ apr_sockaddr_vars_set(sock->remote_addr, family, 0);
+}
+
+static void alloc_socket(apr_socket_t **new, apr_pool_t *p)
+{
+ *new = (apr_socket_t *)apr_pcalloc(p, sizeof(apr_socket_t));
+ (*new)->pool = p;
+ (*new)->local_addr = (apr_sockaddr_t *)apr_pcalloc((*new)->pool,
+ sizeof(apr_sockaddr_t));
+ (*new)->local_addr->pool = p;
+
+ (*new)->remote_addr = (apr_sockaddr_t *)apr_pcalloc((*new)->pool,
+ sizeof(apr_sockaddr_t));
+ (*new)->remote_addr->pool = p;
+ (*new)->remote_addr_unknown = 1;
+
+ /* Create a pollset with room for one descriptor. */
+ /* ### check return codes */
+ (void) apr_pollset_create(&(*new)->pollset, 1, p, 0);
+}
+
+APR_DECLARE(apr_status_t) apr_socket_protocol_get(apr_socket_t *sock, int *protocol)
+{
+ *protocol = sock->protocol;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_create(apr_socket_t **new, int family, int type,
+ int protocol, apr_pool_t *cont)
+{
+ int downgrade = (family == AF_UNSPEC);
+ apr_pollfd_t pfd;
+
+ if (family == AF_UNSPEC) {
+#if APR_HAVE_IPV6
+ family = AF_INET6;
+#else
+ family = AF_INET;
+#endif
+ }
+
+ alloc_socket(new, cont);
+
+ (*new)->socketdes = socket(family, type, protocol);
+#if APR_HAVE_IPV6
+ if ((*new)->socketdes < 0 && downgrade) {
+ family = AF_INET;
+ (*new)->socketdes = socket(family, type, protocol);
+ }
+#endif
+
+ if ((*new)->socketdes < 0) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+ set_socket_vars(*new, family, type, protocol);
+
+ (*new)->timeout = -1;
+ (*new)->nonblock = FALSE;
+ apr_pool_cleanup_register((*new)->pool, (void *)(*new),
+ socket_cleanup, apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_shutdown(apr_socket_t *thesocket,
+ apr_shutdown_how_e how)
+{
+ if (shutdown(thesocket->socketdes, how) == 0) {
+ return APR_SUCCESS;
+ }
+ else {
+ return APR_OS2_STATUS(sock_errno());
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_socket_close(apr_socket_t *thesocket)
+{
+ apr_pool_cleanup_kill(thesocket->pool, thesocket, socket_cleanup);
+ return socket_cleanup(thesocket);
+}
+
+APR_DECLARE(apr_status_t) apr_socket_bind(apr_socket_t *sock,
+ apr_sockaddr_t *sa)
+{
+ if (bind(sock->socketdes,
+ (struct sockaddr *)&sa->sa,
+ sa->salen) == -1)
+ return APR_OS2_STATUS(sock_errno());
+ else {
+ sock->local_addr = sa;
+ /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */
+ if (sock->local_addr->sa.sin.sin_port == 0) { /* no need for ntohs() when comparing w/ 0 */
+ sock->local_port_unknown = 1; /* kernel got us an ephemeral port */
+ }
+ return APR_SUCCESS;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_socket_listen(apr_socket_t *sock,
+ apr_int32_t backlog)
+{
+ if (listen(sock->socketdes, backlog) == -1)
+ return APR_OS2_STATUS(sock_errno());
+ else
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_accept(apr_socket_t **new,
+ apr_socket_t *sock,
+ apr_pool_t *connection_context)
+{
+ alloc_socket(new, connection_context);
+ set_socket_vars(*new, sock->local_addr->sa.sin.sin_family, SOCK_STREAM, sock->protocol);
+
+ (*new)->timeout = -1;
+ (*new)->nonblock = FALSE;
+
+ (*new)->socketdes = accept(sock->socketdes,
+ (struct sockaddr *)&(*new)->remote_addr->sa,
+ &(*new)->remote_addr->salen);
+
+ if ((*new)->socketdes < 0) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+
+ *(*new)->local_addr = *sock->local_addr;
+ (*new)->local_addr->pool = connection_context;
+ (*new)->remote_addr->port = ntohs((*new)->remote_addr->sa.sin.sin_port);
+
+ /* fix up any pointers which are no longer valid */
+ if (sock->local_addr->sa.sin.sin_family == AF_INET) {
+ (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin.sin_addr;
+ }
+
+ apr_pool_cleanup_register((*new)->pool, (void *)(*new),
+ socket_cleanup, apr_pool_cleanup_null);
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_connect(apr_socket_t *sock,
+ apr_sockaddr_t *sa)
+{
+ if ((connect(sock->socketdes, (struct sockaddr *)&sa->sa.sin,
+ sa->salen) < 0) &&
+ (sock_errno() != SOCEINPROGRESS)) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+ else {
+ int namelen = sizeof(sock->local_addr->sa.sin);
+ getsockname(sock->socketdes, (struct sockaddr *)&sock->local_addr->sa.sin,
+ &namelen);
+ sock->remote_addr = sa;
+ return APR_SUCCESS;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_socket_type_get(apr_socket_t *sock, int *type)
+{
+ *type = sock->type;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_data_get(void **data, const char *key,
+ apr_socket_t *sock)
+{
+ sock_userdata_t *cur = sock->userdata;
+
+ *data = NULL;
+
+ while (cur) {
+ if (!strcmp(cur->key, key)) {
+ *data = cur->data;
+ break;
+ }
+ cur = cur->next;
+ }
+
+ return APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_socket_data_set(apr_socket_t *sock, void *data, const char *key,
+ apr_status_t (*cleanup) (void *))
+{
+ sock_userdata_t *new = apr_palloc(sock->pool, sizeof(sock_userdata_t));
+
+ new->key = apr_pstrdup(sock->pool, key);
+ new->data = data;
+ new->next = sock->userdata;
+ sock->userdata = new;
+
+ if (cleanup) {
+ apr_pool_cleanup_register(sock->pool, data, cleanup, cleanup);
+ }
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_os_sock_get(apr_os_sock_t *thesock, apr_socket_t *sock)
+{
+ *thesock = sock->socketdes;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_os_sock_make(apr_socket_t **apr_sock,
+ apr_os_sock_info_t *os_sock_info,
+ apr_pool_t *cont)
+{
+ alloc_socket(apr_sock, cont);
+ set_socket_vars(*apr_sock, os_sock_info->family, os_sock_info->type, os_sock_info->protocol);
+ (*apr_sock)->timeout = -1;
+ (*apr_sock)->socketdes = *os_sock_info->os_sock;
+ if (os_sock_info->local) {
+ memcpy(&(*apr_sock)->local_addr->sa.sin,
+ os_sock_info->local,
+ (*apr_sock)->local_addr->salen);
+ /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */
+ (*apr_sock)->local_addr->port = ntohs((*apr_sock)->local_addr->sa.sin.sin_port);
+ }
+ else {
+ (*apr_sock)->local_port_unknown = (*apr_sock)->local_interface_unknown = 1;
+ }
+ if (os_sock_info->remote) {
+ memcpy(&(*apr_sock)->remote_addr->sa.sin,
+ os_sock_info->remote,
+ (*apr_sock)->remote_addr->salen);
+ /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */
+ (*apr_sock)->remote_addr->port = ntohs((*apr_sock)->remote_addr->sa.sin.sin_port);
+ }
+ else {
+ (*apr_sock)->remote_addr_unknown = 1;
+ }
+
+ apr_pool_cleanup_register((*apr_sock)->pool, (void *)(*apr_sock),
+ socket_cleanup, apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_os_sock_put(apr_socket_t **sock, apr_os_sock_t *thesock, apr_pool_t *cont)
+{
+ if (cont == NULL) {
+ return APR_ENOPOOL;
+ }
+ if ((*sock) == NULL) {
+ alloc_socket(sock, cont);
+ set_socket_vars(*sock, AF_INET, SOCK_STREAM, 0);
+ (*sock)->timeout = -1;
+ }
+
+ (*sock)->local_port_unknown = (*sock)->local_interface_unknown = 1;
+ (*sock)->remote_addr_unknown = 1;
+ (*sock)->socketdes = *thesock;
+ return APR_SUCCESS;
+}
+
+APR_POOL_IMPLEMENT_ACCESSOR(socket);
+
+APR_IMPLEMENT_INHERIT_SET(socket, inherit, pool, socket_cleanup)
+
+APR_IMPLEMENT_INHERIT_UNSET(socket, inherit, pool, socket_cleanup)
+
diff --git a/network_io/os2/sockopt.c b/network_io/os2/sockopt.c
new file mode 100644
index 0000000..094cd24
--- /dev/null
+++ b/network_io/os2/sockopt.c
@@ -0,0 +1,144 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_network_io.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/so_ioctl.h>
+
+
+APR_DECLARE(apr_status_t) apr_socket_timeout_set(apr_socket_t *sock,
+ apr_interval_time_t t)
+{
+ sock->timeout = t;
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_opt_set(apr_socket_t *sock,
+ apr_int32_t opt, apr_int32_t on)
+{
+ int one;
+ struct linger li;
+
+ if (on)
+ one = 1;
+ else
+ one = 0;
+
+ if (opt & APR_SO_KEEPALIVE) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, sizeof(int)) == -1) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+ }
+ if (opt & APR_SO_DEBUG) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(int)) == -1) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+ }
+ if (opt & APR_SO_BROADCAST) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_BROADCAST, (void *)&one, sizeof(int)) == -1) {
+ return APR_FROM_OS_ERROR(sock_errno());
+ }
+ }
+ if (opt & APR_SO_REUSEADDR) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(int)) == -1) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+ }
+ if (opt & APR_SO_SNDBUF) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF, (void *)&on, sizeof(int)) == -1) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+ }
+ if (opt & APR_SO_NONBLOCK) {
+ if (ioctl(sock->socketdes, FIONBIO, (caddr_t)&one, sizeof(one)) == -1) {
+ return APR_OS2_STATUS(sock_errno());
+ } else {
+ sock->nonblock = one;
+ }
+ }
+ if (opt & APR_SO_LINGER) {
+ li.l_onoff = on;
+ li.l_linger = APR_MAX_SECS_TO_LINGER;
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(struct linger)) == -1) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+ }
+ if (opt & APR_TCP_NODELAY) {
+ if (setsockopt(sock->socketdes, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(int)) == -1) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+ }
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_timeout_get(apr_socket_t *sock,
+ apr_interval_time_t *t)
+{
+ *t = sock->timeout;
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_opt_get(apr_socket_t *sock,
+ apr_int32_t opt, apr_int32_t *on)
+{
+ switch(opt) {
+ default:
+ return APR_EINVAL;
+ }
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_atmark(apr_socket_t *sock, int *atmark)
+{
+ int oobmark;
+
+ if (ioctl(sock->socketdes, SIOCATMARK, (void*)&oobmark, sizeof(oobmark)) < 0) {
+ return APR_OS2_STATUS(sock_errno());
+ }
+
+ *atmark = (oobmark != 0);
+
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_gethostname(char *buf, apr_int32_t len,
+ apr_pool_t *cont)
+{
+ if (gethostname(buf, len) == -1) {
+ buf[0] = '\0';
+ return APR_OS2_STATUS(sock_errno());
+ }
+ else if (!memchr(buf, '\0', len)) { /* buffer too small */
+ buf[0] = '\0';
+ return APR_ENAMETOOLONG;
+ }
+ return APR_SUCCESS;
+}
diff --git a/network_io/unix/inet_ntop.c b/network_io/unix/inet_ntop.c
new file mode 100644
index 0000000..78dd3ba
--- /dev/null
+++ b/network_io/unix/inet_ntop.c
@@ -0,0 +1,243 @@
+/* Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "apr_private.h"
+#include "apr_arch_networkio.h"
+#include "apr_strings.h"
+
+#if APR_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if APR_HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if APR_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if APR_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <string.h>
+#if APR_HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+
+#ifndef IN6ADDRSZ
+#define IN6ADDRSZ 16
+#endif
+
+#ifndef INT16SZ
+#define INT16SZ sizeof(apr_int16_t)
+#endif
+
+#ifndef __P
+#define __P(x) x
+#endif
+
+#if !defined(EAFNOSUPPORT) && defined(WSAEAFNOSUPPORT)
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static const char *inet_ntop4 __P((const unsigned char *src, char *dst, apr_size_t size));
+#if APR_HAVE_IPV6
+static const char *inet_ntop6 __P((const unsigned char *src, char *dst, apr_size_t size));
+#endif
+
+/* char *
+ * inet_ntop(af, src, dst, size)
+ * convert a network format address to presentation format.
+ * return:
+ * pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ * Paul Vixie, 1996.
+ */
+const char *
+apr_inet_ntop(int af, const void *src, char *dst, apr_size_t size)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_ntop4(src, dst, size));
+#if APR_HAVE_IPV6
+ case AF_INET6:
+ return (inet_ntop6(src, dst, size));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (NULL);
+ }
+ /* NOTREACHED */
+}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ * format an IPv4 address, more or less like inet_ntoa()
+ * return:
+ * `dst' (as a const)
+ * notes:
+ * (1) uses no statics
+ * (2) takes a u_char* not an in_addr as input
+ * author:
+ * Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop4(const unsigned char *src, char *dst, apr_size_t size)
+{
+ const apr_size_t MIN_SIZE = 16; /* space for 255.255.255.255\0 */
+ int n = 0;
+ char *next = dst;
+
+ if (size < MIN_SIZE) {
+ errno = ENOSPC;
+ return NULL;
+ }
+ do {
+ unsigned char u = *src++;
+ if (u > 99) {
+ *next++ = '0' + u/100;
+ u %= 100;
+ *next++ = '0' + u/10;
+ u %= 10;
+ }
+ else if (u > 9) {
+ *next++ = '0' + u/10;
+ u %= 10;
+ }
+ *next++ = '0' + u;
+ *next++ = '.';
+ n++;
+ } while (n < 4);
+ *--next = 0;
+ return dst;
+}
+
+#if APR_HAVE_IPV6
+/* const char *
+ * inet_ntop6(src, dst, size)
+ * convert IPv6 binary address into presentation (printable) format
+ * author:
+ * Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop6(const unsigned char *src, char *dst, apr_size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
+ struct { int base, len; } best = {-1, 0}, cur = {-1, 0};
+ unsigned int words[IN6ADDRSZ / INT16SZ];
+ int i;
+ const unsigned char *next_src, *src_end;
+ unsigned int *next_dest;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ next_src = src;
+ src_end = src + IN6ADDRSZ;
+ next_dest = words;
+ i = 0;
+ do {
+ unsigned int next_word = (unsigned int)*next_src++;
+ next_word <<= 8;
+ next_word |= (unsigned int)*next_src++;
+ *next_dest++ = next_word;
+
+ if (next_word == 0) {
+ if (cur.base == -1) {
+ cur.base = i;
+ cur.len = 1;
+ }
+ else {
+ cur.len++;
+ }
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len) {
+ best = cur;
+ }
+ cur.base = -1;
+ }
+ }
+
+ i++;
+ } while (next_src < src_end);
+
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len) {
+ best = cur;
+ }
+ }
+ if (best.base != -1 && best.len < 2) {
+ best.base = -1;
+ }
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ);) {
+ /* Are we inside the best run of 0x00's? */
+ if (i == best.base) {
+ *tp++ = ':';
+ i += best.len;
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0) {
+ *tp++ = ':';
+ }
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+ if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) {
+ return (NULL);
+ }
+ tp += strlen(tp);
+ break;
+ }
+ tp += apr_snprintf(tp, sizeof tmp - (tp - tmp), "%x", words[i]);
+ i++;
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) {
+ *tp++ = ':';
+ }
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((apr_size_t)(tp - tmp) > size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ strcpy(dst, tmp);
+ return (dst);
+}
+#endif
diff --git a/network_io/unix/inet_pton.c b/network_io/unix/inet_pton.c
new file mode 100644
index 0000000..d41f749
--- /dev/null
+++ b/network_io/unix/inet_pton.c
@@ -0,0 +1,240 @@
+/* Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "apr_private.h"
+#include "apr_arch_networkio.h"
+
+#if APR_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if APR_HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if APR_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if APR_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <string.h>
+#if APR_HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifndef IN6ADDRSZ
+#define IN6ADDRSZ 16
+#endif
+
+#ifndef INT16SZ
+#define INT16SZ sizeof(apr_int16_t)
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ 4
+#endif
+
+#ifndef __P
+#define __P(x) x
+#endif
+
+#if !defined(EAFNOSUPPORT) && defined(WSAEAFNOSUPPORT)
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4 __P((const char *src, unsigned char *dst));
+#if APR_HAVE_IPV6
+static int inet_pton6 __P((const char *src, unsigned char *dst));
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+apr_inet_pton(int af, const char *src, void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_pton4(src, dst));
+#if APR_HAVE_IPV6
+ case AF_INET6:
+ return (inet_pton6(src, dst));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int new = *tp * 10 + (unsigned int)(pch - digits);
+
+ if (new > 255)
+ return (0);
+ *tp = new;
+ if (! saw_digit) {
+ if (++octets > 4)
+ return (0);
+ saw_digit = 1;
+ }
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return (0);
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return (0);
+ }
+ if (octets < 4)
+ return (0);
+
+ memcpy(dst, tmp, INADDRSZ);
+ return (1);
+}
+
+#if APR_HAVE_IPV6
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ unsigned int val;
+
+ memset((tp = tmp), '\0', IN6ADDRSZ);
+ endp = tp + IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ }
+ if (tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) (val >> 8) & 0xff;
+ *tp++ = (unsigned char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const apr_ssize_t n = tp - colonp;
+ apr_ssize_t i;
+
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ memcpy(dst, tmp, IN6ADDRSZ);
+ return (1);
+}
+#endif
diff --git a/network_io/unix/multicast.c b/network_io/unix/multicast.c
new file mode 100644
index 0000000..a604b06
--- /dev/null
+++ b/network_io/unix/multicast.c
@@ -0,0 +1,313 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_network_io.h"
+#include "apr_support.h"
+#include "apr_portable.h"
+#include "apr_arch_inherit.h"
+
+#ifdef HAVE_GETIFADDRS
+#include <net/if.h>
+#include <ifaddrs.h>
+#endif
+
+#ifdef HAVE_STRUCT_IPMREQ
+static void fill_mip_v4(struct ip_mreq *mip, apr_sockaddr_t *mcast,
+ apr_sockaddr_t *iface)
+{
+ mip->imr_multiaddr = mcast->sa.sin.sin_addr;
+ if (iface == NULL) {
+ mip->imr_interface.s_addr = INADDR_ANY;
+ }
+ else {
+ mip->imr_interface = iface->sa.sin.sin_addr;
+ }
+}
+
+/* This function is only interested in AF_INET6 sockets, so a noop
+ * "return 0" implementation for the !APR_HAVE_IPV6 build is
+ * sufficient. */
+static unsigned int find_if_index(const apr_sockaddr_t *iface)
+{
+ unsigned int index = 0;
+#if defined(HAVE_GETIFADDRS) && APR_HAVE_IPV6
+ struct ifaddrs *ifp, *ifs;
+
+ /**
+ * TODO: getifaddrs is only portable to *BSD and OS X. Using ioctl
+ * and SIOCGIFCONF is needed for Linux/Solaris support.
+ *
+ * There is a wrapper that takes the messy ioctl interface into
+ * getifaddrs. The license is acceptable, but, It is a fairly large
+ * chunk of code.
+ */
+ if (getifaddrs(&ifs) != 0) {
+ return 0;
+ }
+
+ for (ifp = ifs; ifp; ifp = ifp->ifa_next) {
+ if (ifp->ifa_addr != NULL && ifp->ifa_addr->sa_family == AF_INET6) {
+ if (memcmp(&iface->sa.sin6.sin6_addr,
+ &((struct sockaddr_in6*)ifp->ifa_addr)->sin6_addr,
+ sizeof(iface->sa.sin6.sin6_addr)) == 0) {
+ index = if_nametoindex(ifp->ifa_name);
+ break;
+ }
+ }
+ }
+
+ freeifaddrs(ifs);
+#endif
+ return index;
+}
+
+#if APR_HAVE_IPV6
+static void fill_mip_v6(struct ipv6_mreq *mip, const apr_sockaddr_t *mcast,
+ const apr_sockaddr_t *iface)
+{
+ memcpy(&mip->ipv6mr_multiaddr, mcast->ipaddr_ptr,
+ sizeof(mip->ipv6mr_multiaddr));
+
+ if (iface == NULL) {
+ mip->ipv6mr_interface = 0;
+ }
+ else {
+ mip->ipv6mr_interface = find_if_index(iface);
+ }
+}
+
+#endif
+
+static int sock_is_ipv4(apr_socket_t *sock)
+{
+ if (sock->local_addr->family == APR_INET)
+ return 1;
+ return 0;
+}
+
+#if APR_HAVE_IPV6
+static int sock_is_ipv6(apr_socket_t *sock)
+{
+ if (sock->local_addr->family == APR_INET6)
+ return 1;
+ return 0;
+}
+#endif
+
+static apr_status_t do_mcast(int type, apr_socket_t *sock,
+ apr_sockaddr_t *mcast, apr_sockaddr_t *iface,
+ apr_sockaddr_t *source)
+{
+ struct ip_mreq mip4;
+ apr_status_t rv = APR_SUCCESS;
+#if APR_HAVE_IPV6
+ struct ipv6_mreq mip6;
+#endif
+#ifdef GROUP_FILTER_SIZE
+ struct group_source_req mip;
+ int ip_proto;
+#endif
+
+ if (source != NULL) {
+#ifdef GROUP_FILTER_SIZE
+ if (sock_is_ipv4(sock)) {
+ ip_proto = IPPROTO_IP;
+ }
+#if APR_HAVE_IPV6
+ else if (sock_is_ipv6(sock)) {
+ ip_proto = IPPROTO_IPV6;
+ }
+#endif
+ else {
+ return APR_ENOTIMPL;
+ }
+
+ if (type == IP_ADD_MEMBERSHIP)
+ type = MCAST_JOIN_SOURCE_GROUP;
+ else if (type == IP_DROP_MEMBERSHIP)
+ type = MCAST_LEAVE_SOURCE_GROUP;
+ else
+ return APR_ENOTIMPL;
+
+ mip.gsr_interface = find_if_index(iface);
+ memcpy(&mip.gsr_group, mcast->ipaddr_ptr, sizeof(mip.gsr_group));
+ memcpy(&mip.gsr_source, source->ipaddr_ptr, sizeof(mip.gsr_source));
+
+ if (setsockopt(sock->socketdes, ip_proto, type, (const void *) &mip,
+ sizeof(mip)) == -1) {
+ rv = errno;
+ }
+#else
+ /* We do not support Source-Specific Multicast. */
+ return APR_ENOTIMPL;
+#endif
+ }
+ else {
+ if (sock_is_ipv4(sock)) {
+
+ fill_mip_v4(&mip4, mcast, iface);
+
+ if (setsockopt(sock->socketdes, IPPROTO_IP, type,
+ (const void *) &mip4, sizeof(mip4)) == -1) {
+ rv = errno;
+ }
+ }
+#if APR_HAVE_IPV6 && defined(IPV6_JOIN_GROUP) && defined(IPV6_LEAVE_GROUP)
+ else if (sock_is_ipv6(sock)) {
+ if (type == IP_ADD_MEMBERSHIP) {
+ type = IPV6_JOIN_GROUP;
+ }
+ else if (type == IP_DROP_MEMBERSHIP) {
+ type = IPV6_LEAVE_GROUP;
+ }
+ else {
+ return APR_ENOTIMPL;
+ }
+
+ fill_mip_v6(&mip6, mcast, iface);
+
+ if (setsockopt(sock->socketdes, IPPROTO_IPV6, type,
+ (const void *) &mip6, sizeof(mip6)) == -1) {
+ rv = errno;
+ }
+ }
+#endif
+ else {
+ rv = APR_ENOTIMPL;
+ }
+ }
+ return rv;
+}
+
+/* Set the IP_MULTICAST_TTL or IP_MULTICAST_LOOP option, or IPv6
+ * equivalents, for the socket, to the given value. Note that this
+ * function *only works* for those particular option types. */
+static apr_status_t do_mcast_opt(int type, apr_socket_t *sock,
+ apr_byte_t value)
+{
+ apr_status_t rv = APR_SUCCESS;
+
+ if (sock_is_ipv4(sock)) {
+ /* For the IP_MULTICAST_* options, this must be a (char *)
+ * pointer. */
+ if (setsockopt(sock->socketdes, IPPROTO_IP, type,
+ (const void *) &value, sizeof(value)) == -1) {
+ rv = errno;
+ }
+ }
+#if APR_HAVE_IPV6
+ else if (sock_is_ipv6(sock)) {
+ /* For the IPV6_* options, an (int *) pointer must be used. */
+ int ivalue = value;
+
+ if (type == IP_MULTICAST_TTL) {
+ type = IPV6_MULTICAST_HOPS;
+ }
+ else if (type == IP_MULTICAST_LOOP) {
+ type = IPV6_MULTICAST_LOOP;
+ }
+ else {
+ return APR_ENOTIMPL;
+ }
+
+ if (setsockopt(sock->socketdes, IPPROTO_IPV6, type,
+ (const void *) &ivalue, sizeof(ivalue)) == -1) {
+ rv = errno;
+ }
+ }
+#endif
+ else {
+ rv = APR_ENOTIMPL;
+ }
+
+ return rv;
+}
+#endif
+
+APR_DECLARE(apr_status_t) apr_mcast_join(apr_socket_t *sock,
+ apr_sockaddr_t *join,
+ apr_sockaddr_t *iface,
+ apr_sockaddr_t *source)
+{
+#if defined(IP_ADD_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ)
+ return do_mcast(IP_ADD_MEMBERSHIP, sock, join, iface, source);
+#else
+ return APR_ENOTIMPL;
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_mcast_leave(apr_socket_t *sock,
+ apr_sockaddr_t *addr,
+ apr_sockaddr_t *iface,
+ apr_sockaddr_t *source)
+{
+#if defined(IP_DROP_MEMBERSHIP) && defined(HAVE_STRUCT_IPMREQ)
+ return do_mcast(IP_DROP_MEMBERSHIP, sock, addr, iface, source);
+#else
+ return APR_ENOTIMPL;
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_mcast_hops(apr_socket_t *sock, apr_byte_t ttl)
+{
+#if defined(IP_MULTICAST_TTL) && defined(HAVE_STRUCT_IPMREQ)
+ return do_mcast_opt(IP_MULTICAST_TTL, sock, ttl);
+#else
+ return APR_ENOTIMPL;
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_mcast_loopback(apr_socket_t *sock,
+ apr_byte_t opt)
+{
+#if defined(IP_MULTICAST_LOOP) && defined(HAVE_STRUCT_IPMREQ)
+ return do_mcast_opt(IP_MULTICAST_LOOP, sock, opt);
+#else
+ return APR_ENOTIMPL;
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_mcast_interface(apr_socket_t *sock,
+ apr_sockaddr_t *iface)
+{
+#if defined(IP_MULTICAST_IF) && defined(HAVE_STRUCT_IPMREQ)
+ apr_status_t rv = APR_SUCCESS;
+
+ if (sock_is_ipv4(sock)) {
+ if (setsockopt(sock->socketdes, IPPROTO_IP, IP_MULTICAST_IF,
+ (const void *) &iface->sa.sin.sin_addr,
+ sizeof(iface->sa.sin.sin_addr)) == -1) {
+ rv = errno;
+ }
+ }
+#if APR_HAVE_IPV6
+ else if (sock_is_ipv6(sock)) {
+ unsigned int idx = find_if_index(iface);
+ if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ (const void *) &idx, sizeof(idx)) == -1) {
+ rv = errno;
+ }
+ }
+#endif
+ else {
+ rv = APR_ENOTIMPL;
+ }
+ return rv;
+#else
+ return APR_ENOTIMPL;
+#endif
+}
diff --git a/network_io/unix/sendrecv.c b/network_io/unix/sendrecv.c
new file mode 100644
index 0000000..4c0e0a6
--- /dev/null
+++ b/network_io/unix/sendrecv.c
@@ -0,0 +1,1110 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_support.h"
+
+#if APR_HAS_SENDFILE
+/* This file is needed to allow us access to the apr_file_t internals. */
+#include "apr_arch_file_io.h"
+#endif /* APR_HAS_SENDFILE */
+
+/* osreldate.h is only needed on FreeBSD for sendfile detection */
+#if defined(__FreeBSD__)
+#include <osreldate.h>
+#endif
+
+apr_status_t apr_socket_send(apr_socket_t *sock, const char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ if (sock->options & APR_INCOMPLETE_WRITE) {
+ sock->options &= ~APR_INCOMPLETE_WRITE;
+ goto do_select;
+ }
+
+ do {
+ rv = write(sock->socketdes, buf, (*len));
+ } while (rv == -1 && errno == EINTR);
+
+ while (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && (sock->timeout > 0)) {
+ apr_status_t arv;
+do_select:
+ arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ else {
+ do {
+ rv = write(sock->socketdes, buf, (*len));
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ if (rv == -1) {
+ *len = 0;
+ return errno;
+ }
+ if ((sock->timeout > 0) && (rv < *len)) {
+ sock->options |= APR_INCOMPLETE_WRITE;
+ }
+ (*len) = rv;
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
+{
+ apr_ssize_t rv;
+ apr_status_t arv;
+
+ if (sock->options & APR_INCOMPLETE_READ) {
+ sock->options &= ~APR_INCOMPLETE_READ;
+ goto do_select;
+ }
+
+ do {
+ rv = read(sock->socketdes, buf, (*len));
+ } while (rv == -1 && errno == EINTR);
+
+ while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && (sock->timeout > 0)) {
+do_select:
+ arv = apr_wait_for_io_or_timeout(NULL, sock, 1);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ else {
+ do {
+ rv = read(sock->socketdes, buf, (*len));
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ if (rv == -1) {
+ (*len) = 0;
+ return errno;
+ }
+ if ((sock->timeout > 0) && (rv < *len)) {
+ sock->options |= APR_INCOMPLETE_READ;
+ }
+ (*len) = rv;
+ if (rv == 0) {
+ return APR_EOF;
+ }
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_sendto(apr_socket_t *sock, apr_sockaddr_t *where,
+ apr_int32_t flags, const char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ do {
+ rv = sendto(sock->socketdes, buf, (*len), flags,
+ (const struct sockaddr*)&where->sa,
+ where->salen);
+ } while (rv == -1 && errno == EINTR);
+
+ while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && (sock->timeout > 0)) {
+ apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ } else {
+ do {
+ rv = sendto(sock->socketdes, buf, (*len), flags,
+ (const struct sockaddr*)&where->sa,
+ where->salen);
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ if (rv == -1) {
+ *len = 0;
+ return errno;
+ }
+ *len = rv;
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_recvfrom(apr_sockaddr_t *from, apr_socket_t *sock,
+ apr_int32_t flags, char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ from->salen = sizeof(from->sa);
+
+ do {
+ rv = recvfrom(sock->socketdes, buf, (*len), flags,
+ (struct sockaddr*)&from->sa, &from->salen);
+ } while (rv == -1 && errno == EINTR);
+
+ while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && (sock->timeout > 0)) {
+ apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 1);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ } else {
+ do {
+ rv = recvfrom(sock->socketdes, buf, (*len), flags,
+ (struct sockaddr*)&from->sa, &from->salen);
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ if (rv == -1) {
+ (*len) = 0;
+ return errno;
+ }
+
+ /*
+ * Check if we have a valid address. recvfrom() with MSG_PEEK may return
+ * success without filling in the address.
+ */
+ if (from->salen > APR_OFFSETOF(struct sockaddr_in, sin_port)) {
+ apr_sockaddr_vars_set(from, from->sa.sin.sin_family,
+ ntohs(from->sa.sin.sin_port));
+ }
+
+ (*len) = rv;
+ if (rv == 0 && sock->type == SOCK_STREAM) {
+ return APR_EOF;
+ }
+
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_sendv(apr_socket_t * sock, const struct iovec *vec,
+ apr_int32_t nvec, apr_size_t *len)
+{
+#ifdef HAVE_WRITEV
+ apr_ssize_t rv;
+ apr_size_t requested_len = 0;
+ apr_int32_t i;
+
+ for (i = 0; i < nvec; i++) {
+ requested_len += vec[i].iov_len;
+ }
+
+ if (sock->options & APR_INCOMPLETE_WRITE) {
+ sock->options &= ~APR_INCOMPLETE_WRITE;
+ goto do_select;
+ }
+
+ do {
+ rv = writev(sock->socketdes, vec, nvec);
+ } while (rv == -1 && errno == EINTR);
+
+ while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && (sock->timeout > 0)) {
+ apr_status_t arv;
+do_select:
+ arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ else {
+ do {
+ rv = writev(sock->socketdes, vec, nvec);
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ if (rv == -1) {
+ *len = 0;
+ return errno;
+ }
+ if ((sock->timeout > 0) && (rv < requested_len)) {
+ sock->options |= APR_INCOMPLETE_WRITE;
+ }
+ (*len) = rv;
+ return APR_SUCCESS;
+#else
+ *len = vec[0].iov_len;
+ return apr_socket_send(sock, vec[0].iov_base, len);
+#endif
+}
+
+#if APR_HAS_SENDFILE
+
+/* TODO: Verify that all platforms handle the fd the same way,
+ * i.e. that they don't move the file pointer.
+ */
+/* TODO: what should flags be? int_32? */
+
+/* Define a structure to pass in when we have a NULL header value */
+static apr_hdtr_t no_hdtr;
+
+#if (defined(__linux__) || defined(__GNU__)) && defined(HAVE_WRITEV)
+
+apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
+ apr_hdtr_t *hdtr, apr_off_t *offset,
+ apr_size_t *len, apr_int32_t flags)
+{
+ int rv, nbytes = 0, total_hdrbytes, i;
+ apr_status_t arv;
+
+#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64)
+ apr_off_t off = *offset;
+#define sendfile sendfile64
+
+#elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4
+ /* 64-bit apr_off_t but no sendfile64(): fail if trying to send
+ * past the 2Gb limit. */
+ off_t off;
+
+ if ((apr_int64_t)*offset + *len > INT_MAX) {
+ return EINVAL;
+ }
+
+ off = *offset;
+
+#else
+ off_t off = *offset;
+
+ /* Multiple reports have shown sendfile failing with EINVAL if
+ * passed a >=2Gb count value on some 64-bit kernels. It won't
+ * noticably hurt performance to limit each call to <2Gb at a
+ * time, so avoid that issue here: */
+ if (sizeof(off_t) == 8 && *len > INT_MAX) {
+ *len = INT_MAX;
+ }
+#endif
+
+ if (!hdtr) {
+ hdtr = &no_hdtr;
+ }
+
+ if (hdtr->numheaders > 0) {
+ apr_size_t hdrbytes;
+
+ /* cork before writing headers */
+ rv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 1);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ /* Now write the headers */
+ arv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders,
+ &hdrbytes);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return errno;
+ }
+ nbytes += hdrbytes;
+
+ /* If this was a partial write and we aren't doing timeouts,
+ * return now with the partial byte count; this is a non-blocking
+ * socket.
+ */
+ total_hdrbytes = 0;
+ for (i = 0; i < hdtr->numheaders; i++) {
+ total_hdrbytes += hdtr->headers[i].iov_len;
+ }
+ if (hdrbytes < total_hdrbytes) {
+ *len = hdrbytes;
+ return apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
+ }
+ }
+
+ if (sock->options & APR_INCOMPLETE_WRITE) {
+ sock->options &= ~APR_INCOMPLETE_WRITE;
+ goto do_select;
+ }
+
+ do {
+ rv = sendfile(sock->socketdes, /* socket */
+ file->filedes, /* open file descriptor of the file to be sent */
+ &off, /* where in the file to start */
+ *len); /* number of bytes to send */
+ } while (rv == -1 && errno == EINTR);
+
+ while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && (sock->timeout > 0)) {
+do_select:
+ arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ else {
+ do {
+ rv = sendfile(sock->socketdes, /* socket */
+ file->filedes, /* open file descriptor of the file to be sent */
+ &off, /* where in the file to start */
+ *len); /* number of bytes to send */
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+
+ if (rv == -1) {
+ *len = nbytes;
+ rv = errno;
+ apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
+ return rv;
+ }
+
+ nbytes += rv;
+
+ if (rv < *len) {
+ *len = nbytes;
+ arv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
+ if (rv > 0) {
+
+ /* If this was a partial write, return now with the
+ * partial byte count; this is a non-blocking socket.
+ */
+
+ if (sock->timeout > 0) {
+ sock->options |= APR_INCOMPLETE_WRITE;
+ }
+ return arv;
+ }
+ else {
+ /* If the file got smaller mid-request, eventually the offset
+ * becomes equal to the new file size and the kernel returns 0.
+ * Make this an error so the caller knows to log something and
+ * exit.
+ */
+ return APR_EOF;
+ }
+ }
+
+ /* Now write the footers */
+ if (hdtr->numtrailers > 0) {
+ apr_size_t trbytes;
+ arv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers,
+ &trbytes);
+ nbytes += trbytes;
+ if (arv != APR_SUCCESS) {
+ *len = nbytes;
+ rv = errno;
+ apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
+ return rv;
+ }
+ }
+
+ apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
+
+ (*len) = nbytes;
+ return rv < 0 ? errno : APR_SUCCESS;
+}
+
+#elif defined(DARWIN)
+
+/* OS/X Release 10.5 or greater */
+apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
+ apr_hdtr_t *hdtr, apr_off_t *offset,
+ apr_size_t *len, apr_int32_t flags)
+{
+ apr_off_t nbytes = 0;
+ apr_off_t bytes_to_send = *len;
+ apr_off_t bytes_sent = 0;
+ apr_status_t arv;
+ int rv = 0;
+
+ /* Ignore flags for now. */
+ flags = 0;
+
+ if (!hdtr) {
+ hdtr = &no_hdtr;
+ }
+
+ /* OS X can send the headers/footers as part of the system call,
+ * but how it counts bytes isn't documented properly. We use
+ * apr_socket_sendv() instead.
+ */
+ if (hdtr->numheaders > 0) {
+ apr_size_t hbytes;
+ int i;
+
+ /* Now write the headers */
+ arv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders,
+ &hbytes);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return errno;
+ }
+ bytes_sent = hbytes;
+
+ hbytes = 0;
+ for (i = 0; i < hdtr->numheaders; i++) {
+ hbytes += hdtr->headers[i].iov_len;
+ }
+ if (bytes_sent < hbytes) {
+ *len = bytes_sent;
+ return APR_SUCCESS;
+ }
+ }
+
+ do {
+ if (!bytes_to_send) {
+ break;
+ }
+ if (sock->options & APR_INCOMPLETE_WRITE) {
+ apr_status_t arv;
+ sock->options &= ~APR_INCOMPLETE_WRITE;
+ arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ }
+
+ nbytes = bytes_to_send;
+ rv = sendfile(file->filedes, /* file to be sent */
+ sock->socketdes, /* socket */
+ *offset, /* where in the file to start */
+ &nbytes, /* number of bytes to write/written */
+ NULL, /* Headers/footers */
+ flags); /* undefined, set to 0 */
+
+ if (rv == -1) {
+ if (errno == EAGAIN) {
+ if (sock->timeout > 0) {
+ sock->options |= APR_INCOMPLETE_WRITE;
+ }
+ /* BSD's sendfile can return -1/EAGAIN even if it
+ * sent bytes. Sanitize the result so we get normal EAGAIN
+ * semantics w.r.t. bytes sent.
+ */
+ if (nbytes) {
+ bytes_sent += nbytes;
+ /* normal exit for a big file & non-blocking io */
+ (*len) = bytes_sent;
+ return APR_SUCCESS;
+ }
+ }
+ }
+ else { /* rv == 0 (or the kernel is broken) */
+ bytes_sent += nbytes;
+ if (nbytes == 0) {
+ /* Most likely the file got smaller after the stat.
+ * Return an error so the caller can do the Right Thing.
+ */
+ (*len) = bytes_sent;
+ return APR_EOF;
+ }
+ }
+ } while (rv == -1 && (errno == EINTR || errno == EAGAIN));
+
+ /* Now write the footers */
+ if (hdtr->numtrailers > 0) {
+ apr_size_t tbytes;
+ arv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers,
+ &tbytes);
+ bytes_sent += tbytes;
+ if (arv != APR_SUCCESS) {
+ *len = bytes_sent;
+ rv = errno;
+ return rv;
+ }
+ }
+
+ (*len) = bytes_sent;
+ if (rv == -1) {
+ return errno;
+ }
+ return APR_SUCCESS;
+}
+
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+
+/* Release 3.1 or greater */
+apr_status_t apr_socket_sendfile(apr_socket_t * sock, apr_file_t * file,
+ apr_hdtr_t * hdtr, apr_off_t * offset,
+ apr_size_t * len, apr_int32_t flags)
+{
+ off_t nbytes = 0;
+ int rv;
+#if defined(__FreeBSD_version) && __FreeBSD_version < 460001
+ int i;
+#endif
+ struct sf_hdtr headerstruct;
+ apr_size_t bytes_to_send = *len;
+
+ /* Ignore flags for now. */
+ flags = 0;
+
+ if (!hdtr) {
+ hdtr = &no_hdtr;
+ }
+
+#if defined(__FreeBSD_version) && __FreeBSD_version < 460001
+ else if (hdtr->numheaders) {
+
+ /* On early versions of FreeBSD sendfile, the number of bytes to send
+ * must include the length of the headers. Don't look at the man page
+ * for this :( Instead, look at the logic in
+ * src/sys/kern/uipc_syscalls::sendfile().
+ *
+ * This was fixed in the middle of 4.6-STABLE
+ */
+ for (i = 0; i < hdtr->numheaders; i++) {
+ bytes_to_send += hdtr->headers[i].iov_len;
+ }
+ }
+#endif
+
+ headerstruct.headers = hdtr->headers;
+ headerstruct.hdr_cnt = hdtr->numheaders;
+ headerstruct.trailers = hdtr->trailers;
+ headerstruct.trl_cnt = hdtr->numtrailers;
+
+ /* FreeBSD can send the headers/footers as part of the system call */
+ do {
+ if (sock->options & APR_INCOMPLETE_WRITE) {
+ apr_status_t arv;
+ sock->options &= ~APR_INCOMPLETE_WRITE;
+ arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ }
+ if (bytes_to_send) {
+ /* We won't dare call sendfile() if we don't have
+ * header or file bytes to send because bytes_to_send == 0
+ * means send the whole file.
+ */
+ rv = sendfile(file->filedes, /* file to be sent */
+ sock->socketdes, /* socket */
+ *offset, /* where in the file to start */
+ bytes_to_send, /* number of bytes to send */
+ &headerstruct, /* Headers/footers */
+ &nbytes, /* number of bytes written */
+ flags); /* undefined, set to 0 */
+
+ if (rv == -1) {
+ if (errno == EAGAIN) {
+ if (sock->timeout > 0) {
+ sock->options |= APR_INCOMPLETE_WRITE;
+ }
+ /* FreeBSD's sendfile can return -1/EAGAIN even if it
+ * sent bytes. Sanitize the result so we get normal EAGAIN
+ * semantics w.r.t. bytes sent.
+ */
+ if (nbytes) {
+ /* normal exit for a big file & non-blocking io */
+ (*len) = nbytes;
+ return APR_SUCCESS;
+ }
+ }
+ }
+ else { /* rv == 0 (or the kernel is broken) */
+ if (nbytes == 0) {
+ /* Most likely the file got smaller after the stat.
+ * Return an error so the caller can do the Right Thing.
+ */
+ (*len) = nbytes;
+ return APR_EOF;
+ }
+ }
+ }
+ else {
+ /* just trailer bytes... use writev()
+ */
+ rv = writev(sock->socketdes,
+ hdtr->trailers,
+ hdtr->numtrailers);
+ if (rv > 0) {
+ nbytes = rv;
+ rv = 0;
+ }
+ else {
+ nbytes = 0;
+ }
+ }
+ if ((rv == -1) && (errno == EAGAIN)
+ && (sock->timeout > 0)) {
+ apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ }
+ } while (rv == -1 && (errno == EINTR || errno == EAGAIN));
+
+ (*len) = nbytes;
+ if (rv == -1) {
+ return errno;
+ }
+ return APR_SUCCESS;
+}
+
+#elif defined(__hpux) || defined(__hpux__)
+
+/* HP cc in ANSI mode defines __hpux; gcc defines __hpux__ */
+
+/* HP-UX Version 10.30 or greater
+ * (no worries, because we only get here if autoconfiguration found sendfile)
+ */
+
+/* ssize_t sendfile(int s, int fd, off_t offset, size_t nbytes,
+ * const struct iovec *hdtrl, int flags);
+ *
+ * nbytes is the number of bytes to send just from the file; as with FreeBSD,
+ * if nbytes == 0, the rest of the file (from offset) is sent
+ */
+
+apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
+ apr_hdtr_t *hdtr, apr_off_t *offset,
+ apr_size_t *len, apr_int32_t flags)
+{
+ int i;
+ apr_ssize_t rc;
+ apr_size_t nbytes = *len, headerlen, trailerlen;
+ struct iovec hdtrarray[2];
+ char *headerbuf, *trailerbuf;
+
+#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64)
+ /* later HP-UXes have a sendfile64() */
+#define sendfile sendfile64
+ apr_off_t off = *offset;
+
+#elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4
+ /* HP-UX 11.00 doesn't have a sendfile64(): fail if trying to send
+ * past the 2Gb limit */
+ off_t off;
+
+ if ((apr_int64_t)*offset + *len > INT_MAX) {
+ return EINVAL;
+ }
+ off = *offset;
+#else
+ apr_off_t off = *offset;
+#endif
+
+ if (!hdtr) {
+ hdtr = &no_hdtr;
+ }
+
+ /* Ignore flags for now. */
+ flags = 0;
+
+ /* HP-UX can only send one header iovec and one footer iovec; try to
+ * only allocate storage to combine input iovecs when we really have to
+ */
+
+ switch(hdtr->numheaders) {
+ case 0:
+ hdtrarray[0].iov_base = NULL;
+ hdtrarray[0].iov_len = 0;
+ break;
+ case 1:
+ hdtrarray[0] = hdtr->headers[0];
+ break;
+ default:
+ headerlen = 0;
+ for (i = 0; i < hdtr->numheaders; i++) {
+ headerlen += hdtr->headers[i].iov_len;
+ }
+
+ /* XXX: BUHHH? wow, what a memory leak! */
+ headerbuf = hdtrarray[0].iov_base = apr_palloc(sock->pool, headerlen);
+ hdtrarray[0].iov_len = headerlen;
+
+ for (i = 0; i < hdtr->numheaders; i++) {
+ memcpy(headerbuf, hdtr->headers[i].iov_base,
+ hdtr->headers[i].iov_len);
+ headerbuf += hdtr->headers[i].iov_len;
+ }
+ }
+
+ switch(hdtr->numtrailers) {
+ case 0:
+ hdtrarray[1].iov_base = NULL;
+ hdtrarray[1].iov_len = 0;
+ break;
+ case 1:
+ hdtrarray[1] = hdtr->trailers[0];
+ break;
+ default:
+ trailerlen = 0;
+ for (i = 0; i < hdtr->numtrailers; i++) {
+ trailerlen += hdtr->trailers[i].iov_len;
+ }
+
+ /* XXX: BUHHH? wow, what a memory leak! */
+ trailerbuf = hdtrarray[1].iov_base = apr_palloc(sock->pool, trailerlen);
+ hdtrarray[1].iov_len = trailerlen;
+
+ for (i = 0; i < hdtr->numtrailers; i++) {
+ memcpy(trailerbuf, hdtr->trailers[i].iov_base,
+ hdtr->trailers[i].iov_len);
+ trailerbuf += hdtr->trailers[i].iov_len;
+ }
+ }
+
+ do {
+ if (nbytes) { /* any bytes to send from the file? */
+ rc = sendfile(sock->socketdes, /* socket */
+ file->filedes, /* file descriptor to send */
+ off, /* where in the file to start */
+ nbytes, /* number of bytes to send from file */
+ hdtrarray, /* Headers/footers */
+ flags); /* undefined, set to 0 */
+ }
+ else { /* we can't call sendfile() with no bytes to send from the file */
+ rc = writev(sock->socketdes, hdtrarray, 2);
+ }
+ } while (rc == -1 && errno == EINTR);
+
+ while ((rc == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && (sock->timeout > 0)) {
+ apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ else {
+ do {
+ if (nbytes) {
+ rc = sendfile(sock->socketdes, /* socket */
+ file->filedes, /* file descriptor to send */
+ off, /* where in the file to start */
+ nbytes, /* number of bytes to send from file */
+ hdtrarray, /* Headers/footers */
+ flags); /* undefined, set to 0 */
+ }
+ else { /* we can't call sendfile() with no bytes to send from the file */
+ rc = writev(sock->socketdes, hdtrarray, 2);
+ }
+ } while (rc == -1 && errno == EINTR);
+ }
+ }
+
+ if (rc == -1) {
+ *len = 0;
+ return errno;
+ }
+
+ /* Set len to the number of bytes written */
+ *len = rc;
+ return APR_SUCCESS;
+}
+#elif defined(_AIX) || defined(__MVS__)
+/* AIX and OS/390 have the same send_file() interface.
+ *
+ * subtle differences:
+ * AIX doesn't update the file ptr but OS/390 does
+ *
+ * availability (correctly determined by autoconf):
+ *
+ * AIX - version 4.3.2 with APAR IX85388, or version 4.3.3 and above
+ * OS/390 - V2R7 and above
+ */
+apr_status_t apr_socket_sendfile(apr_socket_t * sock, apr_file_t * file,
+ apr_hdtr_t * hdtr, apr_off_t * offset,
+ apr_size_t * len, apr_int32_t flags)
+{
+ int i, ptr, rv = 0;
+ void * hbuf=NULL, * tbuf=NULL;
+ apr_status_t arv;
+ struct sf_parms parms;
+
+ if (!hdtr) {
+ hdtr = &no_hdtr;
+ }
+
+ /* Ignore flags for now. */
+ flags = 0;
+
+ /* word to the wise: by default, AIX stores files sent by send_file()
+ * in the network buffer cache... there are supposedly scenarios
+ * where the most recent copy of the file won't be sent, but I can't
+ * recreate the potential problem, perhaps because of the way we
+ * use send_file()... if you suspect such a problem, try turning
+ * on the SF_SYNC_CACHE flag
+ */
+
+ /* AIX can also send the headers/footers as part of the system call */
+ parms.header_length = 0;
+ if (hdtr && hdtr->numheaders) {
+ if (hdtr->numheaders == 1) {
+ parms.header_data = hdtr->headers[0].iov_base;
+ parms.header_length = hdtr->headers[0].iov_len;
+ }
+ else {
+ for (i = 0; i < hdtr->numheaders; i++) {
+ parms.header_length += hdtr->headers[i].iov_len;
+ }
+#if 0
+ /* Keepalives make apr_palloc a bad idea */
+ hbuf = malloc(parms.header_length);
+#else
+ /* but headers are small, so maybe we can hold on to the
+ * memory for the life of the socket...
+ */
+ hbuf = apr_palloc(sock->pool, parms.header_length);
+#endif
+ ptr = 0;
+ for (i = 0; i < hdtr->numheaders; i++) {
+ memcpy((char *)hbuf + ptr, hdtr->headers[i].iov_base,
+ hdtr->headers[i].iov_len);
+ ptr += hdtr->headers[i].iov_len;
+ }
+ parms.header_data = hbuf;
+ }
+ }
+ else parms.header_data = NULL;
+ parms.trailer_length = 0;
+ if (hdtr && hdtr->numtrailers) {
+ if (hdtr->numtrailers == 1) {
+ parms.trailer_data = hdtr->trailers[0].iov_base;
+ parms.trailer_length = hdtr->trailers[0].iov_len;
+ }
+ else {
+ for (i = 0; i < hdtr->numtrailers; i++) {
+ parms.trailer_length += hdtr->trailers[i].iov_len;
+ }
+#if 0
+ /* Keepalives make apr_palloc a bad idea */
+ tbuf = malloc(parms.trailer_length);
+#else
+ tbuf = apr_palloc(sock->pool, parms.trailer_length);
+#endif
+ ptr = 0;
+ for (i = 0; i < hdtr->numtrailers; i++) {
+ memcpy((char *)tbuf + ptr, hdtr->trailers[i].iov_base,
+ hdtr->trailers[i].iov_len);
+ ptr += hdtr->trailers[i].iov_len;
+ }
+ parms.trailer_data = tbuf;
+ }
+ }
+ else {
+ parms.trailer_data = NULL;
+ }
+
+ /* Whew! Headers and trailers set up. Now for the file data */
+
+ parms.file_descriptor = file->filedes;
+ parms.file_offset = *offset;
+ parms.file_bytes = *len;
+
+ /* O.K. All set up now. Let's go to town */
+
+ if (sock->options & APR_INCOMPLETE_WRITE) {
+ sock->options &= ~APR_INCOMPLETE_WRITE;
+ goto do_select;
+ }
+
+ do {
+ rv = send_file(&(sock->socketdes), /* socket */
+ &(parms), /* all data */
+ flags); /* flags */
+ } while (rv == -1 && errno == EINTR);
+
+ while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
+ && (sock->timeout > 0)) {
+do_select:
+ arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ else {
+ do {
+ rv = send_file(&(sock->socketdes), /* socket */
+ &(parms), /* all data */
+ flags); /* flags */
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+
+ (*len) = parms.bytes_sent;
+
+#if 0
+ /* Clean up after ourselves */
+ if(hbuf) free(hbuf);
+ if(tbuf) free(tbuf);
+#endif
+
+ if (rv == -1) {
+ return errno;
+ }
+
+ if ((sock->timeout > 0)
+ && (parms.bytes_sent
+ < (parms.file_bytes + parms.header_length + parms.trailer_length))) {
+ sock->options |= APR_INCOMPLETE_WRITE;
+ }
+
+ return APR_SUCCESS;
+}
+#elif defined(__osf__) && defined (__alpha)
+/* Tru64's sendfile implementation doesn't work, and we need to make sure that
+ * we don't use it until it is fixed. If it is used as it is now, it will
+ * hang the machine and the only way to fix it is a reboot.
+ */
+#elif defined(HAVE_SENDFILEV)
+/* Solaris 8's sendfilev() interface
+ *
+ * SFV_FD_SELF refers to our memory space.
+ *
+ * Required Sparc patches (or newer):
+ * 111297-01, 108528-09, 109472-06, 109234-03, 108995-02, 111295-01, 109025-03,
+ * 108991-13
+ * Required x86 patches (or newer):
+ * 111298-01, 108529-09, 109473-06, 109235-04, 108996-02, 111296-01, 109026-04,
+ * 108992-13
+ */
+
+#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILEV64)
+#define sendfilevec_t sendfilevec64_t
+#define sendfilev sendfilev64
+#endif
+
+apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
+ apr_hdtr_t *hdtr, apr_off_t *offset,
+ apr_size_t *len, apr_int32_t flags)
+{
+ apr_status_t rv, arv;
+ apr_size_t nbytes;
+ sendfilevec_t *sfv;
+ int vecs, curvec, i, repeat;
+ apr_size_t requested_len = 0;
+
+ if (!hdtr) {
+ hdtr = &no_hdtr;
+ }
+
+ /* Ignore flags for now. */
+ flags = 0;
+
+ /* Calculate how much space we need. */
+ vecs = hdtr->numheaders + hdtr->numtrailers + 1;
+ sfv = apr_palloc(sock->pool, sizeof(sendfilevec_t) * vecs);
+
+ curvec = 0;
+
+ /* Add the headers */
+ for (i = 0; i < hdtr->numheaders; i++, curvec++) {
+ sfv[curvec].sfv_fd = SFV_FD_SELF;
+ sfv[curvec].sfv_flag = 0;
+ /* Cast to unsigned long to prevent sign extension of the
+ * pointer value for the LFS case; see PR 39463. */
+ sfv[curvec].sfv_off = (unsigned long)hdtr->headers[i].iov_base;
+ sfv[curvec].sfv_len = hdtr->headers[i].iov_len;
+ requested_len += sfv[curvec].sfv_len;
+ }
+
+ /* If the len is 0, we skip the file. */
+ if (*len)
+ {
+ sfv[curvec].sfv_fd = file->filedes;
+ sfv[curvec].sfv_flag = 0;
+ sfv[curvec].sfv_off = *offset;
+ sfv[curvec].sfv_len = *len;
+ requested_len += sfv[curvec].sfv_len;
+
+ curvec++;
+ }
+ else {
+ vecs--;
+ }
+
+ /* Add the footers */
+ for (i = 0; i < hdtr->numtrailers; i++, curvec++) {
+ sfv[curvec].sfv_fd = SFV_FD_SELF;
+ sfv[curvec].sfv_flag = 0;
+ sfv[curvec].sfv_off = (unsigned long)hdtr->trailers[i].iov_base;
+ sfv[curvec].sfv_len = hdtr->trailers[i].iov_len;
+ requested_len += sfv[curvec].sfv_len;
+ }
+
+ /* If the last write couldn't send all the requested data,
+ * wait for the socket to become writable before proceeding
+ */
+ if (sock->options & APR_INCOMPLETE_WRITE) {
+ sock->options &= ~APR_INCOMPLETE_WRITE;
+ arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (arv != APR_SUCCESS) {
+ *len = 0;
+ return arv;
+ }
+ }
+
+ /* Actually do the sendfilev
+ *
+ * Solaris may return -1/EAGAIN even if it sent bytes on a non-block sock.
+ *
+ * If no bytes were originally sent (nbytes == 0) and we are on a TIMEOUT
+ * socket (which as far as the OS is concerned is a non-blocking socket),
+ * we want to retry after waiting for the other side to read the data (as
+ * determined by poll). Once it is clear to send, we want to retry
+ * sending the sendfilevec_t once more.
+ */
+ arv = 0;
+ do {
+ /* Clear out the repeat */
+ repeat = 0;
+
+ /* socket, vecs, number of vecs, bytes written */
+ rv = sendfilev(sock->socketdes, sfv, vecs, &nbytes);
+
+ if (rv == -1 && errno == EAGAIN) {
+ if (nbytes) {
+ rv = 0;
+ }
+ else if (!arv && (sock->timeout > 0)) {
+ apr_status_t t = apr_wait_for_io_or_timeout(NULL, sock, 0);
+
+ if (t != APR_SUCCESS) {
+ *len = 0;
+ return t;
+ }
+
+ arv = 1;
+ repeat = 1;
+ }
+ }
+ } while ((rv == -1 && errno == EINTR) || repeat);
+
+ if (rv == -1) {
+ *len = 0;
+ return errno;
+ }
+
+ /* Update how much we sent */
+ *len = nbytes;
+
+ if (nbytes == 0) {
+ /* Most likely the file got smaller after the stat.
+ * Return an error so the caller can do the Right Thing.
+ */
+ return APR_EOF;
+ }
+
+ if ((sock->timeout > 0) && (*len < requested_len)) {
+ sock->options |= APR_INCOMPLETE_WRITE;
+ }
+ return APR_SUCCESS;
+}
+#else
+#error APR has detected sendfile on your system, but nobody has written a
+#error version of it for APR yet. To get past this, either write
+#error apr_socket_sendfile or change APR_HAS_SENDFILE in apr.h to 0.
+#endif /* __linux__, __FreeBSD__, __DragonFly__, __HPUX__, _AIX, __MVS__,
+ Tru64/OSF1 */
+
+#endif /* APR_HAS_SENDFILE */
diff --git a/network_io/unix/sockaddr.c b/network_io/unix/sockaddr.c
new file mode 100644
index 0000000..a31867d
--- /dev/null
+++ b/network_io/unix/sockaddr.c
@@ -0,0 +1,1293 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_strings.h"
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_private.h"
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+#if defined(HAVE_IF_INDEXTONAME) && defined(_MSC_VER)
+#include "arch/win32/apr_arch_misc.h"
+#endif
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+struct apr_ipsubnet_t {
+ int family;
+#if APR_HAVE_IPV6
+ apr_uint32_t sub[4]; /* big enough for IPv4 and IPv6 addresses */
+ apr_uint32_t mask[4];
+#else
+ apr_uint32_t sub[1];
+ apr_uint32_t mask[1];
+#endif
+};
+
+#if !defined(NETWARE) && !defined(WIN32)
+#ifdef HAVE_SET_H_ERRNO
+#define SET_H_ERRNO(newval) set_h_errno(newval)
+#else
+#define SET_H_ERRNO(newval) h_errno = (newval)
+#endif
+#else
+#define SET_H_ERRNO(newval)
+#endif
+
+#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
+ defined(HAVE_GETHOSTBYNAME_R)
+/* This is the maximum size that may be returned from the reentrant
+ * gethostbyname_r function. If the system tries to use more, it
+ * should return ERANGE.
+ */
+#define GETHOSTBYNAME_BUFLEN 512
+#endif
+
+#ifdef _AIX
+/* Some levels of AIX getaddrinfo() don't like servname = "0", so
+ * set servname to "1" when port is 0 and fix it up later.
+ */
+#define AIX_SERVNAME_HACK 1
+#else
+#define AIX_SERVNAME_HACK 0
+#endif
+
+#ifdef _WIN32_WCE
+/* XXX: BS solution. Need an HAVE_GETSERVBYNAME and actually
+ * do something here, to provide the obvious proto mappings.
+ */
+static void *getservbyname(const char *name, const char *proto)
+{
+ return NULL;
+}
+#endif
+
+static apr_status_t get_local_addr(apr_socket_t *sock)
+{
+ sock->local_addr->salen = sizeof(sock->local_addr->sa);
+ if (getsockname(sock->socketdes, (struct sockaddr *)&sock->local_addr->sa,
+ &sock->local_addr->salen) < 0) {
+ return apr_get_netos_error();
+ }
+ else {
+ sock->local_port_unknown = sock->local_interface_unknown = 0;
+ /* XXX assumes sin_port and sin6_port at same offset */
+ sock->local_addr->port = ntohs(sock->local_addr->sa.sin.sin_port);
+ return APR_SUCCESS;
+ }
+}
+
+static apr_status_t get_remote_addr(apr_socket_t *sock)
+{
+ sock->remote_addr->salen = sizeof(sock->remote_addr->sa);
+ if (getpeername(sock->socketdes, (struct sockaddr *)&sock->remote_addr->sa,
+ &sock->remote_addr->salen) < 0) {
+ return apr_get_netos_error();
+ }
+ else {
+ sock->remote_addr_unknown = 0;
+ /* XXX assumes sin_port and sin6_port at same offset */
+ sock->remote_addr->port = ntohs(sock->remote_addr->sa.sin.sin_port);
+ return APR_SUCCESS;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_sockaddr_ip_getbuf(char *buf, apr_size_t buflen,
+ apr_sockaddr_t *sockaddr)
+{
+#if APR_HAVE_SOCKADDR_UN
+ if (sockaddr->family == APR_UNIX) {
+ const char *ptr = sockaddr->ipaddr_ptr;
+ apr_size_t len = apr_cpystrn(buf, ptr, buflen) - buf;
+ /* assumes that sockaddr->ipaddr_ptr is nul terminated */
+ return ptr[len] ? APR_ENOSPC : APR_SUCCESS;
+ }
+#endif
+
+ if (!apr_inet_ntop(sockaddr->family, sockaddr->ipaddr_ptr, buf, buflen)) {
+ return APR_ENOSPC;
+ }
+
+#if APR_HAVE_IPV6
+ if (sockaddr->family == AF_INET6
+ && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)sockaddr->ipaddr_ptr)
+ && buflen > strlen("::ffff:")) {
+ /* This is an IPv4-mapped IPv6 address; drop the leading
+ * part of the address string so we're left with the familiar
+ * IPv4 format.
+ */
+ memmove(buf, buf + strlen("::ffff:"),
+ strlen(buf + strlen("::ffff:"))+1);
+ }
+
+ /* ensure NUL termination if the buffer is too short */
+ buf[buflen-1] = '\0';
+
+#ifdef HAVE_IF_INDEXTONAME
+ /* Append scope name for link-local addresses. */
+ if (sockaddr->family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)sockaddr->ipaddr_ptr)) {
+ char scbuf[IF_NAMESIZE], *p = buf + strlen(buf);
+
+ if (if_indextoname(sockaddr->sa.sin6.sin6_scope_id, scbuf) == scbuf) {
+ /* Space check, need room for buf + '%' + scope + '\0'.
+ * Assert: buflen >= strlen(buf) + strlen(scbuf) + 2
+ * Equiv: buflen >= (p-buf) + strlen(buf) + 2
+ * Thus, fail in inverse condition: */
+ if (buflen < strlen(scbuf) + (p - buf) + 2) {
+ return APR_ENOSPC;
+ }
+ *p++ = '%';
+ memcpy(p, scbuf, strlen(scbuf) + 1);
+ }
+ }
+#endif /* HAVE_IF_INDEXTONAME */
+#endif /* APR_HAVE_IPV6 */
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_sockaddr_ip_get(char **addr,
+ apr_sockaddr_t *sockaddr)
+{
+ *addr = apr_palloc(sockaddr->pool, sockaddr->addr_str_len);
+ return apr_sockaddr_ip_getbuf(*addr, sockaddr->addr_str_len, sockaddr);
+}
+
+void apr_sockaddr_vars_set(apr_sockaddr_t *addr, int family, apr_port_t port)
+{
+ addr->family = family;
+ addr->sa.sin.sin_family = family;
+ if (port) {
+ /* XXX IPv6: assumes sin_port and sin6_port at same offset */
+ addr->sa.sin.sin_port = htons(port);
+ addr->port = port;
+ }
+#if AIX_SERVNAME_HACK
+ else {
+ addr->sa.sin.sin_port = htons(port);
+ }
+#endif
+
+ if (family == APR_INET) {
+ addr->salen = sizeof(struct sockaddr_in);
+ addr->addr_str_len = 16;
+ addr->ipaddr_ptr = &(addr->sa.sin.sin_addr);
+ addr->ipaddr_len = sizeof(struct in_addr);
+ }
+#if APR_HAVE_IPV6
+ else if (family == APR_INET6) {
+ addr->salen = sizeof(struct sockaddr_in6);
+ addr->addr_str_len = 46;
+ addr->ipaddr_ptr = &(addr->sa.sin6.sin6_addr);
+ addr->ipaddr_len = sizeof(struct in6_addr);
+ }
+#endif
+#if APR_HAVE_SOCKADDR_UN
+ else if (family == APR_UNIX) {
+ addr->salen = sizeof(struct sockaddr_un);
+ addr->addr_str_len = sizeof(addr->sa.unx.sun_path);;
+ addr->ipaddr_ptr = &(addr->sa.unx.sun_path);
+ addr->ipaddr_len = addr->addr_str_len;
+ }
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_socket_addr_get(apr_sockaddr_t **sa,
+ apr_interface_e which,
+ apr_socket_t *sock)
+{
+ if (which == APR_LOCAL) {
+ if (sock->local_interface_unknown || sock->local_port_unknown) {
+ apr_status_t rv = get_local_addr(sock);
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ *sa = sock->local_addr;
+ }
+ else if (which == APR_REMOTE) {
+ if (sock->remote_addr_unknown) {
+ apr_status_t rv = get_remote_addr(sock);
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ *sa = sock->remote_addr;
+ }
+ else {
+ *sa = NULL;
+ return APR_EINVAL;
+ }
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_parse_addr_port(char **addr,
+ char **scope_id,
+ apr_port_t *port,
+ const char *str,
+ apr_pool_t *p)
+{
+ const char *ch, *lastchar;
+ int big_port;
+ apr_size_t addrlen;
+
+ *addr = NULL; /* assume not specified */
+ *scope_id = NULL; /* assume not specified */
+ *port = 0; /* assume not specified */
+
+ /* First handle the optional port number. That may be all that
+ * is specified in the string.
+ */
+ ch = lastchar = str + strlen(str) - 1;
+ while (ch >= str && apr_isdigit(*ch)) {
+ --ch;
+ }
+
+ if (ch < str) { /* Entire string is the port. */
+ big_port = atoi(str);
+ if (big_port < 1 || big_port > 65535) {
+ return APR_EINVAL;
+ }
+ *port = big_port;
+ return APR_SUCCESS;
+ }
+
+ if (*ch == ':' && ch < lastchar) { /* host and port number specified */
+ if (ch == str) { /* string starts with ':' -- bad */
+ return APR_EINVAL;
+ }
+ big_port = atoi(ch + 1);
+ if (big_port < 1 || big_port > 65535) {
+ return APR_EINVAL;
+ }
+ *port = big_port;
+ lastchar = ch - 1;
+ }
+
+ /* now handle the hostname */
+ addrlen = lastchar - str + 1;
+
+/* XXX we don't really have to require APR_HAVE_IPV6 for this;
+ * just pass char[] for ipaddr (so we don't depend on struct in6_addr)
+ * and always define APR_INET6
+ */
+#if APR_HAVE_IPV6
+ if (*str == '[') {
+ const char *end_bracket = memchr(str, ']', addrlen);
+ struct in6_addr ipaddr;
+ const char *scope_delim;
+
+ if (!end_bracket || end_bracket != lastchar) {
+ *port = 0;
+ return APR_EINVAL;
+ }
+
+ /* handle scope id; this is the only context where it is allowed */
+ scope_delim = memchr(str, '%', addrlen);
+ if (scope_delim) {
+ if (scope_delim == end_bracket - 1) { /* '%' without scope id */
+ *port = 0;
+ return APR_EINVAL;
+ }
+ addrlen = scope_delim - str - 1;
+ *scope_id = apr_pstrmemdup(p, scope_delim + 1, end_bracket - scope_delim - 1);
+ }
+ else {
+ addrlen = addrlen - 2; /* minus 2 for '[' and ']' */
+ }
+
+ *addr = apr_pstrmemdup(p, str + 1, addrlen);
+ if (apr_inet_pton(AF_INET6, *addr, &ipaddr) != 1) {
+ *addr = NULL;
+ *scope_id = NULL;
+ *port = 0;
+ return APR_EINVAL;
+ }
+ }
+ else
+#endif
+ {
+ /* XXX If '%' is not a valid char in a DNS name, we *could* check
+ * for bogus scope ids first.
+ */
+ *addr = apr_pstrmemdup(p, str, addrlen);
+ }
+ return APR_SUCCESS;
+}
+
+#if defined(HAVE_GETADDRINFO)
+
+static apr_status_t call_resolver(apr_sockaddr_t **sa,
+ const char *hostname, apr_int32_t family,
+ apr_port_t port, apr_int32_t flags,
+ apr_pool_t *p)
+{
+ struct addrinfo hints, *ai, *ai_list;
+ apr_sockaddr_t *prev_sa;
+ int error;
+ char *servname = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+#ifdef HAVE_GAI_ADDRCONFIG
+ if (family == APR_UNSPEC) {
+ /* By default, only look up addresses using address types for
+ * which a local interface is configured, i.e. no IPv6 if no
+ * IPv6 interfaces configured. */
+ hints.ai_flags = AI_ADDRCONFIG;
+ }
+#endif
+
+#ifdef __MVS__
+ /* z/OS will not return IPv4 address under AF_UNSPEC if any IPv6 results
+ * are returned, w/o AI_ALL.
+ */
+ if (family == APR_UNSPEC) {
+ hints.ai_flags |= AI_ALL;
+ }
+#endif
+
+ if(hostname == NULL) {
+#ifdef AI_PASSIVE
+ /* If hostname is NULL, assume we are trying to bind to all
+ * interfaces. */
+ hints.ai_flags |= AI_PASSIVE;
+#endif
+ /* getaddrinfo according to RFC 2553 must have either hostname
+ * or servname non-NULL.
+ */
+#ifdef OSF1
+ /* The Tru64 5.0 getaddrinfo() can only resolve services given
+ * by the name listed in /etc/services; a numeric or unknown
+ * servname gets an EAI_SERVICE error. So just resolve the
+ * appropriate anyaddr and fill in the port later. */
+ hostname = family == AF_INET6 ? "::" : "0.0.0.0";
+ servname = NULL;
+#ifdef AI_NUMERICHOST
+ hints.ai_flags |= AI_NUMERICHOST;
+#endif
+#else
+#if AIX_SERVNAME_HACK
+ if (!port) {
+ servname = "1";
+ }
+ else
+#endif /* AIX_SERVNAME_HACK */
+ servname = apr_itoa(p, port);
+#endif /* OSF1 */
+ }
+ error = getaddrinfo(hostname, servname, &hints, &ai_list);
+#ifdef HAVE_GAI_ADDRCONFIG
+ /*
+ * Using AI_ADDRCONFIG involves some unfortunate guesswork because it
+ * does not consider loopback addresses when trying to determine if
+ * IPv4 or IPv6 is configured on a system (see RFC 3493).
+ * This is a problem if one actually wants to listen on or connect to
+ * the loopback address of a protocol family that is not otherwise
+ * configured on the system. See PR 52709.
+ * To work around some of the problems, retry without AI_ADDRCONFIG
+ * in case of EAI_ADDRFAMILY.
+ * XXX: apr_sockaddr_info_get() should really accept a flag to determine
+ * XXX: if AI_ADDRCONFIG's guesswork is wanted and if the address is
+ * XXX: to be used for listen() or connect().
+ *
+ * In case of EAI_BADFLAGS, AI_ADDRCONFIG is not supported.
+ */
+ if ((family == APR_UNSPEC) && (error == EAI_BADFLAGS
+#ifdef EAI_ADDRFAMILY
+ || error == EAI_ADDRFAMILY
+#endif
+ )) {
+ hints.ai_flags &= ~AI_ADDRCONFIG;
+ error = getaddrinfo(hostname, servname, &hints, &ai_list);
+ }
+#endif
+ if (error) {
+#if defined(WIN32)
+ return apr_get_netos_error();
+#else
+ if (error == EAI_SYSTEM) {
+ return errno ? errno : APR_EGENERAL;
+ }
+ else
+ {
+ /* issues with representing this with APR's error scheme:
+ * glibc uses negative values for these numbers, perhaps so
+ * they don't conflict with h_errno values... Tru64 uses
+ * positive values which conflict with h_errno values
+ */
+#if defined(NEGATIVE_EAI)
+ error = -error;
+#endif
+ return error + APR_OS_START_EAIERR;
+ }
+#endif /* WIN32 */
+ }
+
+ prev_sa = NULL;
+ ai = ai_list;
+ while (ai) { /* while more addresses to report */
+ apr_sockaddr_t *new_sa;
+
+ /* Ignore anything bogus: getaddrinfo in some old versions of
+ * glibc will return AF_UNIX entries for APR_UNSPEC+AI_PASSIVE
+ * lookups. */
+#if APR_HAVE_IPV6
+ if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
+#else
+ if (ai->ai_family != AF_INET) {
+#endif
+ ai = ai->ai_next;
+ continue;
+ }
+
+ new_sa = apr_pcalloc(p, sizeof(apr_sockaddr_t));
+
+ new_sa->pool = p;
+ memcpy(&new_sa->sa, ai->ai_addr, ai->ai_addrlen);
+ apr_sockaddr_vars_set(new_sa, ai->ai_family, port);
+
+ if (!prev_sa) { /* first element in new list */
+ if (hostname) {
+ new_sa->hostname = apr_pstrdup(p, hostname);
+ }
+ *sa = new_sa;
+ }
+ else {
+ new_sa->hostname = prev_sa->hostname;
+ prev_sa->next = new_sa;
+ }
+
+ prev_sa = new_sa;
+ ai = ai->ai_next;
+ }
+ freeaddrinfo(ai_list);
+
+ if (prev_sa == NULL) {
+ /*
+ * getaddrinfo returned only useless entries and *sa is still empty.
+ * This should be treated as an error.
+ */
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t find_addresses(apr_sockaddr_t **sa,
+ const char *hostname, apr_int32_t family,
+ apr_port_t port, apr_int32_t flags,
+ apr_pool_t *p)
+{
+ if (flags & APR_IPV4_ADDR_OK) {
+ apr_status_t error = call_resolver(sa, hostname, AF_INET, port, flags, p);
+
+#if APR_HAVE_IPV6
+ if (error) {
+ family = AF_INET6; /* try again */
+ }
+ else
+#endif
+ return error;
+ }
+#if APR_HAVE_IPV6
+ else if (flags & APR_IPV6_ADDR_OK) {
+ apr_status_t error = call_resolver(sa, hostname, AF_INET6, port, flags, p);
+
+ if (error) {
+ family = AF_INET; /* try again */
+ }
+ else {
+ return APR_SUCCESS;
+ }
+ }
+#endif
+
+ return call_resolver(sa, hostname, family, port, flags, p);
+}
+
+#else /* end of HAVE_GETADDRINFO code */
+
+static apr_status_t find_addresses(apr_sockaddr_t **sa,
+ const char *hostname, apr_int32_t family,
+ apr_port_t port, apr_int32_t flags,
+ apr_pool_t *p)
+{
+ struct hostent *hp;
+ apr_sockaddr_t *prev_sa;
+ int curaddr;
+#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
+ defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
+#ifdef GETHOSTBYNAME_R_HOSTENT_DATA
+ struct hostent_data hd;
+#else
+ /* If you see ERANGE, that means GETHOSBYNAME_BUFLEN needs to be
+ * bumped. */
+ char tmp[GETHOSTBYNAME_BUFLEN];
+#endif
+ int hosterror;
+#endif
+ struct hostent hs;
+ struct in_addr ipaddr;
+ char *addr_list[2];
+ const char *orig_hostname = hostname;
+
+ if (hostname == NULL) {
+ /* if we are given a NULL hostname, assume '0.0.0.0' */
+ hostname = "0.0.0.0";
+ }
+
+ if (*hostname >= '0' && *hostname <= '9' &&
+ strspn(hostname, "0123456789.") == strlen(hostname)) {
+
+ ipaddr.s_addr = inet_addr(hostname);
+ addr_list[0] = (char *)&ipaddr;
+ addr_list[1] = NULL; /* just one IP in list */
+ hs.h_addr_list = (char **)addr_list;
+ hp = &hs;
+ }
+ else {
+#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
+ defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
+#if defined(GETHOSTBYNAME_R_HOSTENT_DATA)
+ /* AIX, HP/UX, D/UX et alia */
+ gethostbyname_r(hostname, &hs, &hd);
+ hp = &hs;
+#else
+#if defined(GETHOSTBYNAME_R_GLIBC2)
+ /* Linux glibc2+ */
+ gethostbyname_r(hostname, &hs, tmp, GETHOSTBYNAME_BUFLEN - 1,
+ &hp, &hosterror);
+#else
+ /* Solaris, Irix et alia */
+ hp = gethostbyname_r(hostname, &hs, tmp, GETHOSTBYNAME_BUFLEN - 1,
+ &hosterror);
+#endif /* !defined(GETHOSTBYNAME_R_GLIBC2) */
+ if (!hp) {
+ return (hosterror + APR_OS_START_SYSERR);
+ }
+#endif /* !defined(GETHOSTBYNAME_R_HOSTENT_DATA) */
+#else
+ hp = gethostbyname(hostname);
+#endif
+
+ if (!hp) {
+#ifdef WIN32
+ return apr_get_netos_error();
+#else
+ return (h_errno + APR_OS_START_SYSERR);
+#endif
+ }
+ }
+
+ prev_sa = NULL;
+ curaddr = 0;
+ while (hp->h_addr_list[curaddr]) {
+ apr_sockaddr_t *new_sa = apr_pcalloc(p, sizeof(apr_sockaddr_t));
+
+ new_sa->pool = p;
+ new_sa->sa.sin.sin_addr = *(struct in_addr *)hp->h_addr_list[curaddr];
+ apr_sockaddr_vars_set(new_sa, AF_INET, port);
+
+ if (!prev_sa) { /* first element in new list */
+ if (orig_hostname) {
+ new_sa->hostname = apr_pstrdup(p, orig_hostname);
+ }
+ *sa = new_sa;
+ }
+ else {
+ new_sa->hostname = prev_sa->hostname;
+ prev_sa->next = new_sa;
+ }
+
+ prev_sa = new_sa;
+ ++curaddr;
+ }
+
+ if (prev_sa == NULL) {
+ /* this should not happen but no result should be treated as error */
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+#endif /* end of !HAVE_GETADDRINFO code */
+
+APR_DECLARE(apr_status_t) apr_sockaddr_info_get(apr_sockaddr_t **sa,
+ const char *hostname,
+ apr_int32_t family, apr_port_t port,
+ apr_int32_t flags, apr_pool_t *p)
+{
+ apr_int32_t masked;
+ *sa = NULL;
+
+ if ((masked = flags & (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK))) {
+ if (!hostname ||
+ family != APR_UNSPEC ||
+ masked == (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK)) {
+ return APR_EINVAL;
+ }
+#if !APR_HAVE_IPV6
+ if (flags & APR_IPV6_ADDR_OK) {
+ return APR_ENOTIMPL;
+ }
+#endif
+ }
+ if (family == APR_UNSPEC && hostname && *hostname == '/') {
+ family = APR_UNIX;
+ }
+ if (family == APR_UNIX) {
+#if APR_HAVE_SOCKADDR_UN
+ if (hostname && *hostname == '/') {
+ *sa = apr_pcalloc(p, sizeof(apr_sockaddr_t));
+ (*sa)->pool = p;
+ apr_cpystrn((*sa)->sa.unx.sun_path, hostname,
+ sizeof((*sa)->sa.unx.sun_path));
+ (*sa)->hostname = apr_pstrdup(p, hostname);
+ (*sa)->family = APR_UNIX;
+ (*sa)->sa.unx.sun_family = APR_UNIX;
+ (*sa)->salen = sizeof(struct sockaddr_un);
+ (*sa)->addr_str_len = sizeof((*sa)->sa.unx.sun_path);
+ (*sa)->ipaddr_ptr = &((*sa)->sa.unx.sun_path);
+ (*sa)->ipaddr_len = (*sa)->addr_str_len;
+
+ return APR_SUCCESS;
+ }
+ else
+#endif
+ {
+ *sa = NULL;
+ return APR_ENOTIMPL;
+ }
+ }
+#if !APR_HAVE_IPV6
+ /* What may happen is that APR is not IPv6-enabled, but we're still
+ * going to call getaddrinfo(), so we have to tell the OS we only
+ * want IPv4 addresses back since we won't know what to do with
+ * IPv6 addresses.
+ */
+ if (family == APR_UNSPEC) {
+ family = APR_INET;
+ }
+#endif
+
+ return find_addresses(sa, hostname, family, port, flags, p);
+}
+
+APR_DECLARE(apr_status_t) apr_sockaddr_info_copy(apr_sockaddr_t **dst,
+ const apr_sockaddr_t *src,
+ apr_pool_t *p)
+{
+ apr_sockaddr_t *d;
+ const apr_sockaddr_t *s;
+
+ for (*dst = d = NULL, s = src; s; s = s->next) {
+ if (!d) {
+ *dst = d = apr_pmemdup(p, s, sizeof *s);
+ }
+ else {
+ d = d->next = apr_pmemdup(p, s, sizeof *s);
+ }
+ if (s->hostname) {
+ if (s == src || s->hostname != src->hostname) {
+ d->hostname = apr_pstrdup(p, s->hostname);
+ }
+ else {
+ d->hostname = (*dst)->hostname;
+ }
+ }
+ if (s->servname) {
+ if (s == src || s->servname != src->servname) {
+ d->servname = apr_pstrdup(p, s->servname);
+ }
+ else {
+ d->servname = (*dst)->servname;
+ }
+ }
+ d->pool = p;
+ apr_sockaddr_vars_set(d, s->family, s->port);
+ }
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_getnameinfo(char **hostname,
+ apr_sockaddr_t *sockaddr,
+ apr_int32_t flags)
+{
+#if defined(HAVE_GETNAMEINFO)
+ int rc;
+#if defined(NI_MAXHOST)
+ char tmphostname[NI_MAXHOST];
+#else
+ char tmphostname[256];
+#endif
+
+ /* don't know if it is portable for getnameinfo() to set h_errno;
+ * clear it then see if it was set */
+ SET_H_ERRNO(0);
+
+ /* default flags are NI_NAMREQD; otherwise, getnameinfo() will return
+ * a numeric address string if it fails to resolve the host name;
+ * that is *not* what we want here
+ *
+ * For IPv4-mapped IPv6 addresses, drop down to IPv4 before calling
+ * getnameinfo() to avoid getnameinfo bugs (MacOS X, glibc).
+ */
+#if APR_HAVE_IPV6
+ if (sockaddr->family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&sockaddr->sa.sin6.sin6_addr)) {
+ struct sockaddr_in tmpsa;
+ tmpsa.sin_family = AF_INET;
+ tmpsa.sin_port = 0;
+ tmpsa.sin_addr.s_addr = ((apr_uint32_t *)sockaddr->ipaddr_ptr)[3];
+#ifdef SIN6_LEN
+ tmpsa.sin_len = sizeof(tmpsa);
+#endif
+
+ rc = getnameinfo((const struct sockaddr *)&tmpsa, sizeof(tmpsa),
+ tmphostname, sizeof(tmphostname), NULL, 0,
+ flags != 0 ? flags : NI_NAMEREQD);
+ }
+#if APR_HAVE_SOCKADDR_UN
+ else if (sockaddr->family == APR_UNIX) {
+ *hostname = sockaddr->hostname;
+ return APR_SUCCESS;
+ }
+#endif
+ else
+#endif
+ rc = getnameinfo((const struct sockaddr *)&sockaddr->sa, sockaddr->salen,
+ tmphostname, sizeof(tmphostname), NULL, 0,
+ flags != 0 ? flags : NI_NAMEREQD);
+ if (rc != 0) {
+ *hostname = NULL;
+
+#ifndef WIN32
+ /* something went wrong. Look at the EAI_ error code */
+ if (rc == EAI_SYSTEM) {
+ /* EAI_SYSTEM System error returned in errno. */
+ /* IMHO, Implementations that set h_errno a simply broken. */
+ if (h_errno) { /* for broken implementations which set h_errno */
+ return h_errno + APR_OS_START_SYSERR;
+ }
+ else { /* "normal" case */
+ return errno + APR_OS_START_SYSERR;
+ }
+ }
+ else
+#endif
+ {
+#if defined(NEGATIVE_EAI)
+ if (rc < 0) rc = -rc;
+#endif
+ return rc + APR_OS_START_EAIERR; /* return the EAI_ error */
+ }
+ }
+ *hostname = sockaddr->hostname = apr_pstrdup(sockaddr->pool,
+ tmphostname);
+ return APR_SUCCESS;
+#else
+#if APR_HAS_THREADS && !defined(GETHOSTBYADDR_IS_THREAD_SAFE) && \
+ defined(HAVE_GETHOSTBYADDR_R) && !defined(BEOS)
+#ifdef GETHOSTBYNAME_R_HOSTENT_DATA
+ struct hostent_data hd;
+#else
+ char tmp[GETHOSTBYNAME_BUFLEN];
+#endif
+ int hosterror;
+ struct hostent hs, *hptr;
+
+#if defined(GETHOSTBYNAME_R_HOSTENT_DATA)
+ /* AIX, HP/UX, D/UX et alia */
+ gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr,
+ sizeof(struct in_addr), AF_INET, &hs, &hd);
+ hptr = &hs;
+#else
+#if defined(GETHOSTBYNAME_R_GLIBC2)
+ /* Linux glibc2+ */
+ gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr,
+ sizeof(struct in_addr), AF_INET,
+ &hs, tmp, GETHOSTBYNAME_BUFLEN - 1, &hptr, &hosterror);
+#else
+ /* Solaris, Irix et alia */
+ hptr = gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr,
+ sizeof(struct in_addr), AF_INET,
+ &hs, tmp, GETHOSTBYNAME_BUFLEN, &hosterror);
+#endif /* !defined(GETHOSTBYNAME_R_GLIBC2) */
+ if (!hptr) {
+ *hostname = NULL;
+ return hosterror + APR_OS_START_SYSERR;
+ }
+#endif /* !defined(GETHOSTBYNAME_R_HOSTENT_DATA) */
+#else
+ struct hostent *hptr;
+ hptr = gethostbyaddr((char *)&sockaddr->sa.sin.sin_addr,
+ sizeof(struct in_addr), AF_INET);
+#endif
+
+ if (hptr) {
+ *hostname = sockaddr->hostname = apr_pstrdup(sockaddr->pool, hptr->h_name);
+ return APR_SUCCESS;
+ }
+ *hostname = NULL;
+#if defined(WIN32)
+ return apr_get_netos_error();
+#elif defined(OS2)
+ return h_errno;
+#else
+ return h_errno + APR_OS_START_SYSERR;
+#endif
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_getservbyname(apr_sockaddr_t *sockaddr,
+ const char *servname)
+{
+#if APR_HAS_THREADS && !defined(GETSERVBYNAME_IS_THREAD_SAFE) && \
+ defined(HAVE_GETSERVBYNAME_R) && \
+ (defined(GETSERVBYNAME_R_GLIBC2) || defined(GETSERVBYNAME_R_SOLARIS) || \
+ defined(GETSERVBYNAME_R_OSF1))
+ struct servent se;
+#if defined(GETSERVBYNAME_R_OSF1)
+ struct servent_data sed;
+
+ memset(&sed, 0, sizeof(sed)); /* must zero fill before use */
+#else
+#if defined(GETSERVBYNAME_R_GLIBC2)
+ struct servent *res;
+#endif
+ char buf[1024];
+#endif
+#else
+ struct servent *se;
+#endif
+
+ if (servname == NULL)
+ return APR_EINVAL;
+
+#if APR_HAS_THREADS && !defined(GETSERVBYNAME_IS_THREAD_SAFE) && \
+ defined(HAVE_GETSERVBYNAME_R) && \
+ (defined(GETSERVBYNAME_R_GLIBC2) || defined(GETSERVBYNAME_R_SOLARIS) || \
+ defined(GETSERVBYNAME_R_OSF1))
+#if defined(GETSERVBYNAME_R_GLIBC2)
+ if (getservbyname_r(servname, NULL,
+ &se, buf, sizeof(buf), &res) == 0 && res != NULL) {
+ sockaddr->port = ntohs(res->s_port);
+ sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
+ sockaddr->sa.sin.sin_port = res->s_port;
+ return APR_SUCCESS;
+ }
+#elif defined(GETSERVBYNAME_R_SOLARIS)
+ if (getservbyname_r(servname, NULL, &se, buf, sizeof(buf)) != NULL) {
+ sockaddr->port = ntohs(se.s_port);
+ sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
+ sockaddr->sa.sin.sin_port = se.s_port;
+ return APR_SUCCESS;
+ }
+#elif defined(GETSERVBYNAME_R_OSF1)
+ if (getservbyname_r(servname, NULL, &se, &sed) == 0) {
+ sockaddr->port = ntohs(se.s_port);
+ sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
+ sockaddr->sa.sin.sin_port = se.s_port;
+ return APR_SUCCESS;
+ }
+#endif
+#else
+ if ((se = getservbyname(servname, NULL)) != NULL){
+ sockaddr->port = ntohs(se->s_port);
+ sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
+ sockaddr->sa.sin.sin_port = se->s_port;
+ return APR_SUCCESS;
+ }
+#endif
+ return APR_ENOENT;
+}
+
+#define V4MAPPED_EQUAL(a,b) \
+((a)->sa.sin.sin_family == AF_INET && \
+ (b)->sa.sin.sin_family == AF_INET6 && \
+ IN6_IS_ADDR_V4MAPPED((struct in6_addr *)(b)->ipaddr_ptr) && \
+ !memcmp((a)->ipaddr_ptr, \
+ &((struct in6_addr *)(b)->ipaddr_ptr)->s6_addr[12], \
+ (a)->ipaddr_len))
+
+#if APR_HAVE_IPV6
+#define SCOPE_OR_ZERO(sa_) ((sa_)->family != AF_INET6 ? 0 : \
+ ((sa_)->sa.sin6.sin6_scope_id))
+#else
+#define SCOPE_OR_ZERO(sa_) (0)
+#endif
+
+APR_DECLARE(int) apr_sockaddr_equal(const apr_sockaddr_t *addr1,
+ const apr_sockaddr_t *addr2)
+{
+ if (addr1->ipaddr_len == addr2->ipaddr_len
+ && !memcmp(addr1->ipaddr_ptr, addr2->ipaddr_ptr, addr1->ipaddr_len)
+ && SCOPE_OR_ZERO(addr1) == SCOPE_OR_ZERO(addr2)) {
+ return 1;
+ }
+#if APR_HAVE_IPV6
+ if (V4MAPPED_EQUAL(addr1, addr2)) {
+ return 1;
+ }
+ if (V4MAPPED_EQUAL(addr2, addr1)) {
+ return 1;
+ }
+#endif
+ return 0; /* not equal */
+}
+
+APR_DECLARE(int) apr_sockaddr_is_wildcard(const apr_sockaddr_t *addr)
+{
+ static const char inaddr_any[
+#if APR_HAVE_IPV6
+ sizeof(struct in6_addr)
+#else
+ sizeof(struct in_addr)
+#endif
+ ] = {0};
+
+ if (addr->ipaddr_ptr /* IP address initialized */
+ && addr->ipaddr_len <= sizeof inaddr_any) { /* else bug elsewhere? */
+ if (!memcmp(inaddr_any, addr->ipaddr_ptr, addr->ipaddr_len)) {
+ return 1;
+ }
+#if APR_HAVE_IPV6
+ if (addr->family == AF_INET6
+ && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr)) {
+ struct in_addr *v4 = (struct in_addr *)&((apr_uint32_t *)addr->ipaddr_ptr)[3];
+
+ if (!memcmp(inaddr_any, v4, sizeof *v4)) {
+ return 1;
+ }
+ }
+#endif
+ }
+ return 0;
+}
+
+static apr_status_t parse_network(apr_ipsubnet_t *ipsub, const char *network)
+{
+ /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
+ int shift;
+ char *s, *t;
+ int octet;
+ char buf[sizeof "255.255.255.255"];
+
+ if (strlen(network) < sizeof buf) {
+ strcpy(buf, network);
+ }
+ else {
+ return APR_EBADIP;
+ }
+
+ /* parse components */
+ s = buf;
+ ipsub->sub[0] = 0;
+ ipsub->mask[0] = 0;
+ shift = 24;
+ while (*s) {
+ t = s;
+ if (!apr_isdigit(*t)) {
+ return APR_EBADIP;
+ }
+ while (apr_isdigit(*t)) {
+ ++t;
+ }
+ if (*t == '.') {
+ *t++ = 0;
+ }
+ else if (*t) {
+ return APR_EBADIP;
+ }
+ if (shift < 0) {
+ return APR_EBADIP;
+ }
+ octet = atoi(s);
+ if (octet < 0 || octet > 255) {
+ return APR_EBADIP;
+ }
+ ipsub->sub[0] |= octet << shift;
+ ipsub->mask[0] |= 0xFFUL << shift;
+ s = t;
+ shift -= 8;
+ }
+ ipsub->sub[0] = ntohl(ipsub->sub[0]);
+ ipsub->mask[0] = ntohl(ipsub->mask[0]);
+ ipsub->family = AF_INET;
+ return APR_SUCCESS;
+}
+
+/* return values:
+ * APR_EINVAL not an IP address; caller should see if it is something else
+ * APR_BADIP IP address portion is is not valid
+ * APR_BADMASK mask portion is not valid
+ */
+
+static apr_status_t parse_ip(apr_ipsubnet_t *ipsub, const char *ipstr, int network_allowed)
+{
+ /* supported flavors of IP:
+ *
+ * . IPv6 numeric address string (e.g., "fe80::1")
+ *
+ * IMPORTANT: Don't store IPv4-mapped IPv6 address as an IPv6 address.
+ *
+ * . IPv4 numeric address string (e.g., "127.0.0.1")
+ *
+ * . IPv4 network string (e.g., "9.67")
+ *
+ * IMPORTANT: This network form is only allowed if network_allowed is on.
+ */
+ int rc;
+
+#if APR_HAVE_IPV6
+ rc = apr_inet_pton(AF_INET6, ipstr, ipsub->sub);
+ if (rc == 1) {
+ if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ipsub->sub)) {
+ /* apr_ipsubnet_test() assumes that we don't create IPv4-mapped IPv6
+ * addresses; this of course forces the user to specify IPv4 addresses
+ * in a.b.c.d style instead of ::ffff:a.b.c.d style.
+ */
+ return APR_EBADIP;
+ }
+ ipsub->family = AF_INET6;
+ }
+ else
+#endif
+ {
+ rc = apr_inet_pton(AF_INET, ipstr, ipsub->sub);
+ if (rc == 1) {
+ ipsub->family = AF_INET;
+ }
+ }
+ if (rc != 1) {
+ if (network_allowed) {
+ return parse_network(ipsub, ipstr);
+ }
+ else {
+ return APR_EBADIP;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static int looks_like_ip(const char *ipstr)
+{
+ if (strlen(ipstr) == 0) {
+ return 0;
+ }
+
+ if (strchr(ipstr, ':')) {
+ /* definitely not a hostname; assume it is intended to be an IPv6 address */
+ return 1;
+ }
+
+ /* simple IPv4 address string check */
+ while ((*ipstr == '.') || apr_isdigit(*ipstr))
+ ipstr++;
+ return (*ipstr == '\0');
+}
+
+static void fix_subnet(apr_ipsubnet_t *ipsub)
+{
+ /* in case caller specified more bits in network address than are
+ * valid according to the mask, turn off the extra bits
+ */
+ int i;
+
+ for (i = 0; i < sizeof ipsub->mask / sizeof(apr_int32_t); i++) {
+ ipsub->sub[i] &= ipsub->mask[i];
+ }
+}
+
+/* be sure not to store any IPv4 address as a v4-mapped IPv6 address */
+APR_DECLARE(apr_status_t) apr_ipsubnet_create(apr_ipsubnet_t **ipsub, const char *ipstr,
+ const char *mask_or_numbits, apr_pool_t *p)
+{
+ apr_status_t rv;
+ char *endptr;
+ long bits, maxbits = 32;
+
+ /* filter out stuff which doesn't look remotely like an IP address; this helps
+ * callers like mod_access which have a syntax allowing hostname or IP address;
+ * APR_EINVAL tells the caller that it was probably not intended to be an IP
+ * address
+ */
+ if (!looks_like_ip(ipstr)) {
+ return APR_EINVAL;
+ }
+
+ *ipsub = apr_pcalloc(p, sizeof(apr_ipsubnet_t));
+
+ /* assume ipstr is an individual IP address, not a subnet */
+ memset((*ipsub)->mask, 0xFF, sizeof (*ipsub)->mask);
+
+ rv = parse_ip(*ipsub, ipstr, mask_or_numbits == NULL);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ if (mask_or_numbits) {
+#if APR_HAVE_IPV6
+ if ((*ipsub)->family == AF_INET6) {
+ maxbits = 128;
+ }
+#endif
+ bits = strtol(mask_or_numbits, &endptr, 10);
+ if (*endptr == '\0' && bits > 0 && bits <= maxbits) {
+ /* valid num-bits string; fill in mask appropriately */
+ int cur_entry = 0;
+ apr_int32_t cur_bit_value;
+
+ memset((*ipsub)->mask, 0, sizeof (*ipsub)->mask);
+ while (bits > 32) {
+ (*ipsub)->mask[cur_entry] = 0xFFFFFFFF; /* all 32 bits */
+ bits -= 32;
+ ++cur_entry;
+ }
+ cur_bit_value = 0x80000000;
+ while (bits) {
+ (*ipsub)->mask[cur_entry] |= cur_bit_value;
+ --bits;
+ cur_bit_value /= 2;
+ }
+ (*ipsub)->mask[cur_entry] = htonl((*ipsub)->mask[cur_entry]);
+ }
+ else if (apr_inet_pton(AF_INET, mask_or_numbits, (*ipsub)->mask) == 1 &&
+ (*ipsub)->family == AF_INET) {
+ /* valid IPv4 netmask */
+ }
+ else {
+ return APR_EBADMASK;
+ }
+ }
+
+ fix_subnet(*ipsub);
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(int) apr_ipsubnet_test(apr_ipsubnet_t *ipsub, apr_sockaddr_t *sa)
+{
+#if APR_HAVE_IPV6
+ /* XXX This line will segv on Win32 build with APR_HAVE_IPV6,
+ * but without the IPV6 drivers installed.
+ */
+ if (sa->family == AF_INET) {
+ if (ipsub->family == AF_INET &&
+ ((sa->sa.sin.sin_addr.s_addr & ipsub->mask[0]) == ipsub->sub[0])) {
+ return 1;
+ }
+ }
+ else if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)sa->ipaddr_ptr)) {
+ if (ipsub->family == AF_INET &&
+ (((apr_uint32_t *)sa->ipaddr_ptr)[3] & ipsub->mask[0]) == ipsub->sub[0]) {
+ return 1;
+ }
+ }
+ else if (sa->family == AF_INET6 && ipsub->family == AF_INET6) {
+ apr_uint32_t *addr = (apr_uint32_t *)sa->ipaddr_ptr;
+
+ if ((addr[0] & ipsub->mask[0]) == ipsub->sub[0] &&
+ (addr[1] & ipsub->mask[1]) == ipsub->sub[1] &&
+ (addr[2] & ipsub->mask[2]) == ipsub->sub[2] &&
+ (addr[3] & ipsub->mask[3]) == ipsub->sub[3]) {
+ return 1;
+ }
+ }
+#else
+ if ((sa->sa.sin.sin_addr.s_addr & ipsub->mask[0]) == ipsub->sub[0]) {
+ return 1;
+ }
+#endif /* APR_HAVE_IPV6 */
+ return 0; /* no match */
+}
+
+APR_DECLARE(apr_status_t) apr_sockaddr_zone_set(apr_sockaddr_t *sa,
+ const char *zone_id)
+{
+#if !APR_HAVE_IPV6 || !defined(HAVE_IF_NAMETOINDEX)
+ return APR_ENOTIMPL;
+#else
+ unsigned int idx;
+
+ if (sa->family != APR_INET6
+ || !IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)sa->ipaddr_ptr)) {
+ return APR_EBADIP;
+ }
+
+ idx = if_nametoindex(zone_id);
+ if (idx) {
+ sa->sa.sin6.sin6_scope_id = idx;
+ return APR_SUCCESS;
+ }
+
+ if (errno != ENODEV) {
+ return errno;
+ }
+ else {
+ char *endptr;
+ apr_int64_t i = apr_strtoi64(zone_id, &endptr, 10);
+
+ if (*endptr != '\0' || errno || i < 1 || i > APR_INT16_MAX) {
+ return APR_EGENERAL;
+ }
+
+ sa->sa.sin6.sin6_scope_id = (unsigned int) i;
+ return APR_SUCCESS;
+ }
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_sockaddr_zone_get(const apr_sockaddr_t *sa,
+ const char **name,
+ apr_uint32_t *id,
+ apr_pool_t *p)
+{
+#if !APR_HAVE_IPV6 || !defined(HAVE_IF_INDEXTONAME)
+ return APR_ENOTIMPL;
+#else
+ if (sa->family != APR_INET6 || !sa->sa.sin6.sin6_scope_id) {
+ return APR_EBADIP;
+ }
+
+ if (name) {
+ char *buf = apr_palloc(p, IF_NAMESIZE);
+ if (if_indextoname(sa->sa.sin6.sin6_scope_id, buf) == NULL)
+ return errno;
+ *name = buf;
+ }
+
+ if (id) *id = sa->sa.sin6.sin6_scope_id;
+
+ return APR_SUCCESS;
+#endif
+}
diff --git a/network_io/unix/socket_util.c b/network_io/unix/socket_util.c
new file mode 100644
index 0000000..93fe259
--- /dev/null
+++ b/network_io/unix/socket_util.c
@@ -0,0 +1,75 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_network_io.h"
+#include "apr_poll.h"
+
+APR_DECLARE(apr_status_t) apr_socket_atreadeof(apr_socket_t *sock, int *atreadeof)
+{
+ apr_pollfd_t pfds[1];
+ apr_status_t rv;
+ apr_int32_t nfds;
+
+ /* The purpose here is to return APR_SUCCESS only in cases in
+ * which it can be unambiguously determined whether or not the
+ * socket will return EOF on next read. In case of an unexpected
+ * error, return that. */
+
+ pfds[0].reqevents = APR_POLLIN;
+ pfds[0].desc_type = APR_POLL_SOCKET;
+ pfds[0].desc.s = sock;
+
+ do {
+ rv = apr_poll(&pfds[0], 1, &nfds, 0);
+ } while (APR_STATUS_IS_EINTR(rv));
+
+ if (APR_STATUS_IS_TIMEUP(rv)) {
+ /* Read buffer empty -> subsequent reads would block, so,
+ * definitely not at EOF. */
+ *atreadeof = 0;
+ return APR_SUCCESS;
+ }
+ else if (rv) {
+ /* Some other error -> unexpected error. */
+ return rv;
+ }
+ /* Many platforms return only APR_POLLIN; OS X returns APR_POLLHUP|APR_POLLIN */
+ else if (nfds == 1 && (pfds[0].rtnevents & APR_POLLIN) == APR_POLLIN) {
+ apr_sockaddr_t unused;
+ apr_size_t len = 1;
+ char buf;
+
+ /* The socket is readable - peek to see whether it returns EOF
+ * without consuming bytes from the socket buffer. */
+ rv = apr_socket_recvfrom(&unused, sock, MSG_PEEK, &buf, &len);
+ if (rv == APR_EOF) {
+ *atreadeof = 1;
+ return APR_SUCCESS;
+ }
+ else if (rv) {
+ /* Read error -> unexpected error. */
+ return rv;
+ }
+ else {
+ *atreadeof = 0;
+ return APR_SUCCESS;
+ }
+ }
+
+ /* Should not fall through here. */
+ return APR_EGENERAL;
+}
+
diff --git a/network_io/unix/sockets.c b/network_io/unix/sockets.c
new file mode 100644
index 0000000..206c654
--- /dev/null
+++ b/network_io/unix/sockets.c
@@ -0,0 +1,572 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_network_io.h"
+#include "apr_strings.h"
+#include "apr_support.h"
+#include "apr_portable.h"
+#include "apr_arch_inherit.h"
+
+#ifdef BEOS_R5
+#undef close
+#define close closesocket
+#endif /* BEOS_R5 */
+
+#if APR_HAVE_SOCKADDR_UN
+#define GENERIC_INADDR_ANY_LEN sizeof(struct sockaddr_un)
+#else
+#define GENERIC_INADDR_ANY_LEN 16
+#endif
+
+/* big enough for IPv4, IPv6 and optionaly sun_path */
+static char generic_inaddr_any[GENERIC_INADDR_ANY_LEN] = {0};
+
+static apr_status_t socket_cleanup(void *sock)
+{
+ apr_socket_t *thesocket = sock;
+ int sd = thesocket->socketdes;
+
+#if APR_HAVE_SOCKADDR_UN
+ if (thesocket->bound && thesocket->local_addr->family == APR_UNIX) {
+ /* XXX: Check for return values ? */
+ unlink(thesocket->local_addr->hostname);
+ }
+#endif
+ /* Set socket descriptor to -1 before close(), so that there is no
+ * chance of returning an already closed FD from apr_os_sock_get().
+ */
+ thesocket->socketdes = -1;
+
+ if (close(sd) == 0) {
+ return APR_SUCCESS;
+ }
+ else {
+ /* Restore, close() was not successful. */
+ thesocket->socketdes = sd;
+
+ return errno;
+ }
+}
+
+static apr_status_t socket_child_cleanup(void *sock)
+{
+ apr_socket_t *thesocket = sock;
+ if (close(thesocket->socketdes) == 0) {
+ thesocket->socketdes = -1;
+ return APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+static void set_socket_vars(apr_socket_t *sock, int family, int type, int protocol)
+{
+ sock->type = type;
+ sock->protocol = protocol;
+ apr_sockaddr_vars_set(sock->local_addr, family, 0);
+ apr_sockaddr_vars_set(sock->remote_addr, family, 0);
+ sock->options = 0;
+#if defined(BEOS) && !defined(BEOS_BONE)
+ /* BeOS pre-BONE has TCP_NODELAY on by default and it can't be
+ * switched off!
+ */
+ sock->options |= APR_TCP_NODELAY;
+#endif
+}
+
+static void alloc_socket(apr_socket_t **new, apr_pool_t *p)
+{
+ *new = (apr_socket_t *)apr_pcalloc(p, sizeof(apr_socket_t));
+ (*new)->pool = p;
+ (*new)->local_addr = (apr_sockaddr_t *)apr_pcalloc((*new)->pool,
+ sizeof(apr_sockaddr_t));
+ (*new)->local_addr->pool = p;
+ (*new)->remote_addr = (apr_sockaddr_t *)apr_pcalloc((*new)->pool,
+ sizeof(apr_sockaddr_t));
+ (*new)->remote_addr->pool = p;
+ (*new)->remote_addr_unknown = 1;
+#ifndef WAITIO_USES_POLL
+ /* Create a pollset with room for one descriptor. */
+ /* ### check return codes */
+ (void) apr_pollset_create(&(*new)->pollset, 1, p, 0);
+#endif
+}
+
+apr_status_t apr_socket_protocol_get(apr_socket_t *sock, int *protocol)
+{
+ *protocol = sock->protocol;
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_create(apr_socket_t **new, int ofamily, int type,
+ int protocol, apr_pool_t *cont)
+{
+ int family = ofamily, flags = 0;
+ int oprotocol = protocol;
+
+#ifdef HAVE_SOCK_CLOEXEC
+ flags |= SOCK_CLOEXEC;
+#endif
+
+ if (family == APR_UNSPEC) {
+#if APR_HAVE_IPV6
+ family = APR_INET6;
+#else
+ family = APR_INET;
+#endif
+ }
+#if APR_HAVE_SOCKADDR_UN
+ if (family == APR_UNIX) {
+ protocol = 0;
+ }
+#endif
+ alloc_socket(new, cont);
+
+#ifndef BEOS_R5
+ (*new)->socketdes = socket(family, type|flags, protocol);
+#else
+ /* For some reason BeOS R5 has an unconventional protocol numbering,
+ * so we need to translate here. */
+ switch (protocol) {
+ case 0:
+ (*new)->socketdes = socket(family, type|flags, 0);
+ break;
+ case APR_PROTO_TCP:
+ (*new)->socketdes = socket(family, type|flags, IPPROTO_TCP);
+ break;
+ case APR_PROTO_UDP:
+ (*new)->socketdes = socket(family, type|flags, IPPROTO_UDP);
+ break;
+ case APR_PROTO_SCTP:
+ default:
+ errno = EPROTONOSUPPORT;
+ (*new)->socketdes = -1;
+ break;
+ }
+#endif /* BEOS_R5 */
+
+#if APR_HAVE_IPV6
+ if ((*new)->socketdes < 0 && ofamily == APR_UNSPEC) {
+ family = APR_INET;
+ (*new)->socketdes = socket(family, type|flags, protocol);
+ }
+#endif
+
+ if ((*new)->socketdes < 0) {
+ return errno;
+ }
+ set_socket_vars(*new, family, type, oprotocol);
+
+#ifndef HAVE_SOCK_CLOEXEC
+ {
+ int flags;
+ apr_status_t rv;
+
+ if ((flags = fcntl((*new)->socketdes, F_GETFD)) == -1) {
+ rv = errno;
+ close((*new)->socketdes);
+ (*new)->socketdes = -1;
+ return rv;
+ }
+
+ flags |= FD_CLOEXEC;
+ if (fcntl((*new)->socketdes, F_SETFD, flags) == -1) {
+ rv = errno;
+ close((*new)->socketdes);
+ (*new)->socketdes = -1;
+ return rv;
+ }
+ }
+#endif
+
+ (*new)->timeout = -1;
+ (*new)->inherit = 0;
+ apr_pool_cleanup_register((*new)->pool, (void *)(*new), socket_cleanup,
+ socket_child_cleanup);
+
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_shutdown(apr_socket_t *thesocket,
+ apr_shutdown_how_e how)
+{
+ return (shutdown(thesocket->socketdes, how) == -1) ? errno : APR_SUCCESS;
+}
+
+apr_status_t apr_socket_close(apr_socket_t *thesocket)
+{
+ return apr_pool_cleanup_run(thesocket->pool, thesocket, socket_cleanup);
+}
+
+apr_status_t apr_socket_bind(apr_socket_t *sock, apr_sockaddr_t *sa)
+{
+ if (bind(sock->socketdes,
+ (struct sockaddr *)&sa->sa, sa->salen) == -1) {
+ return errno;
+ }
+ else {
+ sock->local_addr = sa;
+ /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */
+#if APR_HAVE_SOCKADDR_UN
+ if (sock->local_addr->family == APR_UNIX) {
+ sock->bound = 1;
+ sock->local_port_unknown = 1;
+ }
+ else
+#endif
+ if (sock->local_addr->sa.sin.sin_port == 0) { /* no need for ntohs() when comparing w/ 0 */
+ sock->local_port_unknown = 1; /* kernel got us an ephemeral port */
+ }
+ return APR_SUCCESS;
+ }
+}
+
+apr_status_t apr_socket_listen(apr_socket_t *sock, apr_int32_t backlog)
+{
+ if (listen(sock->socketdes, backlog) == -1)
+ return errno;
+ else
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_accept(apr_socket_t **new, apr_socket_t *sock,
+ apr_pool_t *connection_context)
+{
+ int s;
+ apr_sockaddr_t sa;
+
+ sa.salen = sizeof(sa.sa);
+
+#ifdef HAVE_ACCEPT4
+ {
+ int flags = SOCK_CLOEXEC;
+
+#if defined(SOCK_NONBLOCK) && APR_O_NONBLOCK_INHERITED
+ /* With FreeBSD accept4() (avail in 10+), O_NONBLOCK is not inherited
+ * (unlike Linux). Mimic the accept() behavior here in a way that
+ * may help other platforms.
+ */
+ if (apr_is_option_set(sock, APR_SO_NONBLOCK) == 1) {
+ flags |= SOCK_NONBLOCK;
+ }
+#endif
+ s = accept4(sock->socketdes, (struct sockaddr *)&sa.sa, &sa.salen, flags);
+ }
+#else
+ s = accept(sock->socketdes, (struct sockaddr *)&sa.sa, &sa.salen);
+#endif
+
+ if (s < 0) {
+ return errno;
+ }
+#ifdef TPF
+ if (s == 0) {
+ /* 0 is an invalid socket for TPF */
+ return APR_EINTR;
+ }
+#endif
+ alloc_socket(new, connection_context);
+
+ /* Set up socket variables -- note that it may be possible for
+ * *new to be an AF_INET socket when sock is AF_INET6 in some
+ * dual-stack configurations, so ensure that the remote_/local_addr
+ * structures are adjusted for the family of the accepted
+ * socket: */
+ set_socket_vars(*new, sa.sa.sin.sin_family, SOCK_STREAM, sock->protocol);
+
+#ifndef HAVE_POLL
+ (*new)->connected = 1;
+#endif
+ (*new)->timeout = -1;
+
+ (*new)->remote_addr_unknown = 0;
+
+ (*new)->socketdes = s;
+
+ /* Copy in peer's address. */
+ (*new)->remote_addr->sa = sa.sa;
+ (*new)->remote_addr->salen = sa.salen;
+
+ *(*new)->local_addr = *sock->local_addr;
+
+ /* The above assignment just overwrote the pool entry. Setting the local_addr
+ pool for the accepted socket back to what it should be. Otherwise all
+ allocations for this socket will come from a server pool that is not
+ freed until the process goes down.*/
+ (*new)->local_addr->pool = connection_context;
+
+ /* fix up any pointers which are no longer valid */
+ if (sock->local_addr->sa.sin.sin_family == AF_INET) {
+ (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin.sin_addr;
+ }
+#if APR_HAVE_IPV6
+ else if (sock->local_addr->sa.sin.sin_family == AF_INET6) {
+ (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin6.sin6_addr;
+ }
+#endif
+#if APR_HAVE_SOCKADDR_UN
+ else if (sock->local_addr->sa.sin.sin_family == AF_UNIX) {
+ *(*new)->remote_addr = *sock->local_addr;
+ (*new)->local_addr->ipaddr_ptr = &((*new)->local_addr->sa.unx.sun_path);
+ (*new)->remote_addr->ipaddr_ptr = &((*new)->remote_addr->sa.unx.sun_path);
+ }
+ if (sock->local_addr->sa.sin.sin_family != AF_UNIX)
+#endif
+ (*new)->remote_addr->port = ntohs((*new)->remote_addr->sa.sin.sin_port);
+ if (sock->local_port_unknown) {
+ /* not likely for a listening socket, but theoretically possible :) */
+ (*new)->local_port_unknown = 1;
+ }
+
+#if APR_TCP_NODELAY_INHERITED
+ if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1) {
+ apr_set_option(*new, APR_TCP_NODELAY, 1);
+ }
+#endif /* TCP_NODELAY_INHERITED */
+#if APR_O_NONBLOCK_INHERITED
+ if (apr_is_option_set(sock, APR_SO_NONBLOCK) == 1) {
+ apr_set_option(*new, APR_SO_NONBLOCK, 1);
+ }
+#endif /* APR_O_NONBLOCK_INHERITED */
+
+ if (sock->local_interface_unknown ||
+ !memcmp(sock->local_addr->ipaddr_ptr,
+ generic_inaddr_any,
+ sock->local_addr->ipaddr_len)) {
+ /* If the interface address inside the listening socket's local_addr wasn't
+ * up-to-date, we don't know local interface of the connected socket either.
+ *
+ * If the listening socket was not bound to a specific interface, we
+ * don't know the local_addr of the connected socket.
+ */
+ (*new)->local_interface_unknown = 1;
+ }
+
+#ifndef HAVE_ACCEPT4
+ {
+ int flags;
+ apr_status_t rv;
+
+ if ((flags = fcntl((*new)->socketdes, F_GETFD)) == -1) {
+ rv = errno;
+ close((*new)->socketdes);
+ (*new)->socketdes = -1;
+ return rv;
+ }
+
+ flags |= FD_CLOEXEC;
+ if (fcntl((*new)->socketdes, F_SETFD, flags) == -1) {
+ rv = errno;
+ close((*new)->socketdes);
+ (*new)->socketdes = -1;
+ return rv;
+ }
+ }
+#endif
+
+ (*new)->inherit = 0;
+ apr_pool_cleanup_register((*new)->pool, (void *)(*new), socket_cleanup,
+ socket_cleanup);
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_connect(apr_socket_t *sock, apr_sockaddr_t *sa)
+{
+ int rc;
+
+ do {
+ rc = connect(sock->socketdes,
+ (const struct sockaddr *)&sa->sa.sin,
+ sa->salen);
+ } while (rc == -1 && errno == EINTR);
+
+ /* we can see EINPROGRESS the first time connect is called on a non-blocking
+ * socket; if called again, we can see EALREADY
+ */
+ if ((rc == -1) && (errno == EINPROGRESS || errno == EALREADY)
+ && (sock->timeout > 0)) {
+ rc = apr_wait_for_io_or_timeout(NULL, sock, 0);
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
+
+#ifdef SO_ERROR
+ {
+ int error;
+ apr_socklen_t len = sizeof(error);
+ if ((rc = getsockopt(sock->socketdes, SOL_SOCKET, SO_ERROR,
+ (char *)&error, &len)) < 0) {
+ return errno;
+ }
+ if (error) {
+ return error;
+ }
+ }
+#endif /* SO_ERROR */
+ }
+
+ if (memcmp(sa->ipaddr_ptr, generic_inaddr_any, sa->ipaddr_len)) {
+ /* A real remote address was passed in. If the unspecified
+ * address was used, the actual remote addr will have to be
+ * determined using getpeername() if required. */
+ sock->remote_addr_unknown = 0;
+
+ /* Copy the address structure details in. */
+ sock->remote_addr->sa = sa->sa;
+ sock->remote_addr->salen = sa->salen;
+ /* Adjust ipaddr_ptr et al. */
+ apr_sockaddr_vars_set(sock->remote_addr, sa->family, sa->port);
+ }
+
+ if (sock->local_addr->port == 0) {
+ /* connect() got us an ephemeral port */
+ sock->local_port_unknown = 1;
+ }
+#if APR_HAVE_SOCKADDR_UN
+ if (sock->local_addr->sa.sin.sin_family == AF_UNIX) {
+ /* Assign connect address as local. */
+ sock->local_addr = sa;
+ }
+ else
+#endif
+ if (!memcmp(sock->local_addr->ipaddr_ptr,
+ generic_inaddr_any,
+ sock->local_addr->ipaddr_len)) {
+ /* not bound to specific local interface; connect() had to assign
+ * one for the socket
+ */
+ sock->local_interface_unknown = 1;
+ }
+
+ if (rc == -1 && errno != EISCONN) {
+ return errno;
+ }
+
+#ifndef HAVE_POLL
+ sock->connected=1;
+#endif
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_type_get(apr_socket_t *sock, int *type)
+{
+ *type = sock->type;
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_data_get(void **data, const char *key, apr_socket_t *sock)
+{
+ sock_userdata_t *cur = sock->userdata;
+
+ *data = NULL;
+
+ while (cur) {
+ if (!strcmp(cur->key, key)) {
+ *data = cur->data;
+ break;
+ }
+ cur = cur->next;
+ }
+
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_socket_data_set(apr_socket_t *sock, void *data, const char *key,
+ apr_status_t (*cleanup) (void *))
+{
+ sock_userdata_t *new = apr_palloc(sock->pool, sizeof(sock_userdata_t));
+
+ new->key = apr_pstrdup(sock->pool, key);
+ new->data = data;
+ new->next = sock->userdata;
+ sock->userdata = new;
+
+ if (cleanup) {
+ apr_pool_cleanup_register(sock->pool, data, cleanup, cleanup);
+ }
+
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_os_sock_get(apr_os_sock_t *thesock, apr_socket_t *sock)
+{
+ *thesock = sock->socketdes;
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_os_sock_make(apr_socket_t **apr_sock,
+ apr_os_sock_info_t *os_sock_info,
+ apr_pool_t *cont)
+{
+ alloc_socket(apr_sock, cont);
+ set_socket_vars(*apr_sock, os_sock_info->family, os_sock_info->type, os_sock_info->protocol);
+ (*apr_sock)->timeout = -1;
+ (*apr_sock)->socketdes = *os_sock_info->os_sock;
+ if (os_sock_info->local) {
+ memcpy(&(*apr_sock)->local_addr->sa.sin,
+ os_sock_info->local,
+ (*apr_sock)->local_addr->salen);
+ /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */
+ (*apr_sock)->local_addr->port = ntohs((*apr_sock)->local_addr->sa.sin.sin_port);
+ }
+ else {
+ (*apr_sock)->local_port_unknown = (*apr_sock)->local_interface_unknown = 1;
+ }
+ if (os_sock_info->remote) {
+#ifndef HAVE_POLL
+ (*apr_sock)->connected = 1;
+#endif
+ memcpy(&(*apr_sock)->remote_addr->sa.sin,
+ os_sock_info->remote,
+ (*apr_sock)->remote_addr->salen);
+ /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */
+ (*apr_sock)->remote_addr->port = ntohs((*apr_sock)->remote_addr->sa.sin.sin_port);
+ }
+ else {
+ (*apr_sock)->remote_addr_unknown = 1;
+ }
+
+ (*apr_sock)->inherit = 0;
+ apr_pool_cleanup_register((*apr_sock)->pool, (void *)(*apr_sock),
+ socket_cleanup, socket_cleanup);
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_os_sock_put(apr_socket_t **sock, apr_os_sock_t *thesock,
+ apr_pool_t *cont)
+{
+ /* XXX Bogus assumption that *sock points at anything legit */
+ if ((*sock) == NULL) {
+ alloc_socket(sock, cont);
+ /* XXX IPv6 figure out the family here! */
+ /* XXX figure out the actual socket type here */
+ /* *or* just decide that apr_os_sock_put() has to be told the family and type */
+ set_socket_vars(*sock, APR_INET, SOCK_STREAM, 0);
+ (*sock)->timeout = -1;
+ }
+ (*sock)->local_port_unknown = (*sock)->local_interface_unknown = 1;
+ (*sock)->remote_addr_unknown = 1;
+ (*sock)->socketdes = *thesock;
+ return APR_SUCCESS;
+}
+
+APR_POOL_IMPLEMENT_ACCESSOR(socket)
+
+APR_IMPLEMENT_INHERIT_SET(socket, inherit, pool, socket_cleanup)
+
+APR_IMPLEMENT_INHERIT_UNSET(socket, inherit, pool, socket_cleanup)
diff --git a/network_io/unix/sockopt.c b/network_io/unix/sockopt.c
new file mode 100644
index 0000000..6194e9b
--- /dev/null
+++ b/network_io/unix/sockopt.c
@@ -0,0 +1,465 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_strings.h"
+
+
+static apr_status_t soblock(int sd)
+{
+/* BeOS uses setsockopt at present for non blocking... */
+#ifndef BEOS
+ int fd_flags;
+
+ fd_flags = fcntl(sd, F_GETFL, 0);
+#if defined(O_NONBLOCK)
+ fd_flags &= ~O_NONBLOCK;
+#elif defined(O_NDELAY)
+ fd_flags &= ~O_NDELAY;
+#elif defined(FNDELAY)
+ fd_flags &= ~FNDELAY;
+#else
+#error Please teach APR how to make sockets blocking on your platform.
+#endif
+ if (fcntl(sd, F_SETFL, fd_flags) == -1) {
+ return errno;
+ }
+#else
+ int on = 0;
+ if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
+ return errno;
+#endif /* BEOS */
+ return APR_SUCCESS;
+}
+
+static apr_status_t sononblock(int sd)
+{
+#ifndef BEOS
+ int fd_flags;
+
+ fd_flags = fcntl(sd, F_GETFL, 0);
+#if defined(O_NONBLOCK)
+ fd_flags |= O_NONBLOCK;
+#elif defined(O_NDELAY)
+ fd_flags |= O_NDELAY;
+#elif defined(FNDELAY)
+ fd_flags |= FNDELAY;
+#else
+#error Please teach APR how to make sockets non-blocking on your platform.
+#endif
+ if (fcntl(sd, F_SETFL, fd_flags) == -1) {
+ return errno;
+ }
+#else
+ int on = 1;
+ if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
+ return errno;
+#endif /* BEOS */
+ return APR_SUCCESS;
+}
+
+
+apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
+{
+ apr_status_t stat;
+
+ /* If our new timeout is non-negative and our old timeout was
+ * negative, then we need to ensure that we are non-blocking.
+ * Conversely, if our new timeout is negative and we had
+ * non-negative timeout, we must make sure our socket is blocking.
+ * We want to avoid calling fcntl more than necessary on the
+ * socket.
+ */
+ if (t >= 0 && sock->timeout < 0) {
+ if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 1) {
+ if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) {
+ return stat;
+ }
+ apr_set_option(sock, APR_SO_NONBLOCK, 1);
+ }
+ }
+ else if (t < 0 && sock->timeout >= 0) {
+ if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 0) {
+ if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) {
+ return stat;
+ }
+ apr_set_option(sock, APR_SO_NONBLOCK, 0);
+ }
+ }
+ /* must disable the incomplete read support if we disable
+ * a timeout
+ */
+ if (t <= 0) {
+ sock->options &= ~APR_INCOMPLETE_READ;
+ }
+ sock->timeout = t;
+ return APR_SUCCESS;
+}
+
+
+apr_status_t apr_socket_opt_set(apr_socket_t *sock,
+ apr_int32_t opt, apr_int32_t on)
+{
+ int one;
+ apr_status_t rv;
+
+ if (on)
+ one = 1;
+ else
+ one = 0;
+ switch(opt) {
+ case APR_SO_KEEPALIVE:
+#ifdef SO_KEEPALIVE
+ if (on != apr_is_option_set(sock, APR_SO_KEEPALIVE)) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_SO_KEEPALIVE, on);
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_SO_DEBUG:
+ if (on != apr_is_option_set(sock, APR_SO_DEBUG)) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_DEBUG, (void *)&one, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_SO_DEBUG, on);
+ }
+ break;
+ case APR_SO_BROADCAST:
+#ifdef SO_BROADCAST
+ if (on != apr_is_option_set(sock, APR_SO_BROADCAST)) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_BROADCAST, (void *)&one, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_SO_BROADCAST, on);
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_SO_REUSEADDR:
+ if (on != apr_is_option_set(sock, APR_SO_REUSEADDR)) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_SO_REUSEADDR, on);
+ }
+ break;
+ case APR_SO_SNDBUF:
+#ifdef SO_SNDBUF
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF, (void *)&on, sizeof(int)) == -1) {
+ return errno;
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_SO_RCVBUF:
+#ifdef SO_RCVBUF
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVBUF, (void *)&on, sizeof(int)) == -1) {
+ return errno;
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_SO_NONBLOCK:
+ if (apr_is_option_set(sock, APR_SO_NONBLOCK) != on) {
+ if (on) {
+ if ((rv = sononblock(sock->socketdes)) != APR_SUCCESS)
+ return rv;
+ }
+ else {
+ if ((rv = soblock(sock->socketdes)) != APR_SUCCESS)
+ return rv;
+ }
+ apr_set_option(sock, APR_SO_NONBLOCK, on);
+ }
+ break;
+ case APR_SO_LINGER:
+#ifdef SO_LINGER
+ if (apr_is_option_set(sock, APR_SO_LINGER) != on) {
+ struct linger li;
+ li.l_onoff = on;
+ li.l_linger = APR_MAX_SECS_TO_LINGER;
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(struct linger)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_SO_LINGER, on);
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_TCP_DEFER_ACCEPT:
+#if defined(TCP_DEFER_ACCEPT)
+ if (apr_is_option_set(sock, APR_TCP_DEFER_ACCEPT) != on) {
+ int optlevel = IPPROTO_TCP;
+ int optname = TCP_DEFER_ACCEPT;
+
+ if (setsockopt(sock->socketdes, optlevel, optname,
+ (void *)&on, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_TCP_DEFER_ACCEPT, on);
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_TCP_NODELAY:
+#if defined(TCP_NODELAY)
+ if (apr_is_option_set(sock, APR_TCP_NODELAY) != on) {
+ int optlevel = IPPROTO_TCP;
+ int optname = TCP_NODELAY;
+
+#if APR_HAVE_SCTP
+ if (sock->protocol == IPPROTO_SCTP) {
+ optlevel = IPPROTO_SCTP;
+ optname = SCTP_NODELAY;
+ }
+#endif
+ if (setsockopt(sock->socketdes, optlevel, optname, (void *)&on, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_TCP_NODELAY, on);
+ }
+#else
+ /* BeOS pre-BONE has TCP_NODELAY set by default.
+ * As it can't be turned off we might as well check if they're asking
+ * for it to be turned on!
+ */
+#ifdef BEOS
+ if (on == 1)
+ return APR_SUCCESS;
+ else
+#endif
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_TCP_NOPUSH:
+#if APR_TCP_NOPUSH_FLAG
+ /* TCP_NODELAY and TCP_CORK are mutually exclusive on Linux
+ * kernels < 2.6; on newer kernels they can be used together
+ * and TCP_CORK takes preference, which is the desired
+ * behaviour. On older kernels, TCP_NODELAY must be toggled
+ * to "off" whilst TCP_CORK is in effect. */
+ if (apr_is_option_set(sock, APR_TCP_NOPUSH) != on) {
+#ifndef HAVE_TCP_NODELAY_WITH_CORK
+ int optlevel = IPPROTO_TCP;
+ int optname = TCP_NODELAY;
+
+#if APR_HAVE_SCTP
+ if (sock->protocol == IPPROTO_SCTP) {
+ optlevel = IPPROTO_SCTP;
+ optname = SCTP_NODELAY;
+ }
+#endif
+ /* OK we're going to change some settings here... */
+ if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1 && on) {
+ /* Now toggle TCP_NODELAY to off, if TCP_CORK is being
+ * turned on: */
+ int tmpflag = 0;
+ if (setsockopt(sock->socketdes, optlevel, optname,
+ (void*)&tmpflag, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_RESET_NODELAY, 1);
+ apr_set_option(sock, APR_TCP_NODELAY, 0);
+ } else if (on) {
+ apr_set_option(sock, APR_RESET_NODELAY, 0);
+ }
+#endif /* HAVE_TCP_NODELAY_WITH_CORK */
+
+ /* OK, now we can just set the TCP_NOPUSH flag accordingly...*/
+ if (setsockopt(sock->socketdes, IPPROTO_TCP, APR_TCP_NOPUSH_FLAG,
+ (void*)&on, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_TCP_NOPUSH, on);
+#ifndef HAVE_TCP_NODELAY_WITH_CORK
+ if (!on && apr_is_option_set(sock, APR_RESET_NODELAY)) {
+ /* Now, if TCP_CORK was just turned off, turn
+ * TCP_NODELAY back on again if it was earlier toggled
+ * to off: */
+ int tmpflag = 1;
+ if (setsockopt(sock->socketdes, optlevel, optname,
+ (void*)&tmpflag, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_RESET_NODELAY,0);
+ apr_set_option(sock, APR_TCP_NODELAY, 1);
+ }
+#endif /* HAVE_TCP_NODELAY_WITH_CORK */
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_INCOMPLETE_READ:
+ apr_set_option(sock, APR_INCOMPLETE_READ, on);
+ break;
+ case APR_IPV6_V6ONLY:
+#if APR_HAVE_IPV6 && defined(IPV6_V6ONLY)
+ /* we don't know the initial setting of this option,
+ * so don't check sock->options since that optimization
+ * won't work
+ */
+ if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY,
+ (void *)&on, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_IPV6_V6ONLY, on);
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ case APR_SO_FREEBIND:
+#if defined(IP_FREEBIND)
+ if (setsockopt(sock->socketdes, SOL_IP, IP_FREEBIND,
+ (void *)&one, sizeof(int)) == -1) {
+ return errno;
+ }
+ apr_set_option(sock, APR_SO_FREEBIND, on);
+#elif 0 /* defined(IP_BINDANY) ... */
+ /* TODO: insert FreeBSD support here, note family specific
+ * options, IP_BINDANY vs IPV6_BINDANY */
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ default:
+ return APR_EINVAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+
+apr_status_t apr_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
+{
+ *t = sock->timeout;
+ return APR_SUCCESS;
+}
+
+
+apr_status_t apr_socket_opt_get(apr_socket_t *sock,
+ apr_int32_t opt, apr_int32_t *on)
+{
+ switch(opt) {
+ default:
+ *on = apr_is_option_set(sock, opt);
+ }
+ return APR_SUCCESS;
+}
+
+
+apr_status_t apr_socket_atmark(apr_socket_t *sock, int *atmark)
+{
+#ifndef BEOS_R5
+ int oobmark;
+
+ if (ioctl(sock->socketdes, SIOCATMARK, (void*) &oobmark) < 0)
+ return apr_get_netos_error();
+
+ *atmark = (oobmark != 0);
+
+ return APR_SUCCESS;
+#else /* BEOS_R5 */
+ return APR_ENOTIMPL;
+#endif
+}
+
+apr_status_t apr_gethostname(char *buf, apr_int32_t len, apr_pool_t *cont)
+{
+#ifdef BEOS_R5
+ if (gethostname(buf, len) == 0) {
+#else
+ if (gethostname(buf, len) != 0) {
+#endif
+ buf[0] = '\0';
+ return errno;
+ }
+ else if (!memchr(buf, '\0', len)) { /* buffer too small */
+ /* note... most platforms just truncate in this condition
+ * linux+glibc return an error
+ */
+ buf[0] = '\0';
+ return APR_ENAMETOOLONG;
+ }
+ return APR_SUCCESS;
+}
+
+#if APR_HAS_SO_ACCEPTFILTER
+apr_status_t apr_socket_accept_filter(apr_socket_t *sock, char *nonconst_name,
+ char *nonconst_args)
+{
+ /* these should have been const; act like they are */
+ const char *name = nonconst_name;
+ const char *args = nonconst_args;
+
+ struct accept_filter_arg af;
+ socklen_t optlen = sizeof(af);
+
+ /* FreeBSD returns an error if the filter is already set; ignore
+ * this call if we previously set it to the same value.
+ */
+ if ((getsockopt(sock->socketdes, SOL_SOCKET, SO_ACCEPTFILTER,
+ &af, &optlen)) == 0) {
+ if (!strcmp(name, af.af_name) && !strcmp(args, af.af_arg)) {
+ return APR_SUCCESS;
+ }
+ }
+
+ /* Uhh, at least in FreeBSD 9 the fields are declared as arrays of
+ * these lengths; did sizeof not work in some ancient release?
+ *
+ * FreeBSD kernel sets the last byte to a '\0'.
+ */
+ apr_cpystrn(af.af_name, name, 16);
+ apr_cpystrn(af.af_arg, args, 256 - 16);
+
+ if ((setsockopt(sock->socketdes, SOL_SOCKET, SO_ACCEPTFILTER,
+ &af, sizeof(af))) < 0) {
+ return errno;
+ }
+ return APR_SUCCESS;
+}
+#endif
+
+APR_PERMS_SET_IMPLEMENT(socket)
+{
+#if APR_HAVE_SOCKADDR_UN
+ apr_status_t rv = APR_SUCCESS;
+ apr_socket_t *socket = (apr_socket_t *)thesocket;
+
+ if (socket->local_addr->family == APR_UNIX) {
+ if (!(perms & APR_FPROT_GSETID))
+ gid = -1;
+ if (fchown(socket->socketdes, uid, gid) < 0) {
+ rv = errno;
+ }
+ }
+ else
+ rv = APR_EINVAL;
+ return rv;
+#else
+ return APR_ENOTIMPL;
+#endif
+}
diff --git a/network_io/win32/sendrecv.c b/network_io/win32/sendrecv.c
new file mode 100644
index 0000000..6620e13
--- /dev/null
+++ b/network_io/win32/sendrecv.c
@@ -0,0 +1,442 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_network_io.h"
+#include "apr_lib.h"
+#include "apr_arch_file_io.h"
+#if APR_HAVE_TIME_H
+#include <time.h>
+#endif
+
+/* MAX_SEGMENT_SIZE is the maximum amount of data that will be sent to a client
+ * in one call of TransmitFile. This number must be small enough to give the
+ * slowest client time to receive the data before the socket timeout triggers.
+ * The same problem can exist with apr_socket_send(). In that case, we rely on
+ * the application to adjust socket timeouts and max send segment
+ * sizes appropriately.
+ * For example, Apache will in most cases call apr_socket_send() with less
+ * than 8193 bytes.
+ */
+#define MAX_SEGMENT_SIZE 65536
+#define WSABUF_ON_STACK 50
+
+APR_DECLARE(apr_status_t) apr_socket_send(apr_socket_t *sock, const char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+ WSABUF wsaData;
+ int lasterror;
+ DWORD dwBytes = 0;
+
+ wsaData.len = (u_long)*len;
+ wsaData.buf = (char*) buf;
+
+#ifndef _WIN32_WCE
+ rv = WSASend(sock->socketdes, &wsaData, 1, &dwBytes, 0, NULL, NULL);
+#else
+ rv = send(sock->socketdes, wsaData.buf, wsaData.len, 0);
+ dwBytes = rv;
+#endif
+ if (rv == SOCKET_ERROR) {
+ lasterror = apr_get_netos_error();
+ *len = 0;
+ return lasterror;
+ }
+
+ *len = dwBytes;
+
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_recv(apr_socket_t *sock, char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+ WSABUF wsaData;
+ int lasterror;
+ DWORD dwBytes = 0;
+ DWORD flags = 0;
+
+ wsaData.len = (u_long)*len;
+ wsaData.buf = (char*) buf;
+
+#ifndef _WIN32_WCE
+ rv = WSARecv(sock->socketdes, &wsaData, 1, &dwBytes, &flags, NULL, NULL);
+#else
+ rv = recv(sock->socketdes, wsaData.buf, wsaData.len, 0);
+ dwBytes = rv;
+#endif
+ if (rv == SOCKET_ERROR) {
+ lasterror = apr_get_netos_error();
+ *len = 0;
+ return lasterror;
+ }
+
+ *len = dwBytes;
+ return dwBytes == 0 ? APR_EOF : APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_sendv(apr_socket_t *sock,
+ const struct iovec *vec,
+ apr_int32_t in_vec, apr_size_t *nbytes)
+{
+ apr_status_t rc = APR_SUCCESS;
+ apr_ssize_t rv;
+ apr_size_t total_len;
+ apr_int32_t i;
+ DWORD dwBytes = 0;
+ WSABUF *pWsaBuf;
+
+ total_len = 0;
+ for (i = 0; i < in_vec; i++) {
+ apr_size_t iov_len = vec[i].iov_len;
+ if (iov_len > (apr_size_t)MAXDWORD - total_len) {
+ /* WSASend() returns NumberOfBytesSent as DWORD, so the total size
+ should be less than that. */
+ return APR_EINVAL;
+ }
+ total_len += iov_len;
+ }
+
+ pWsaBuf = (in_vec <= WSABUF_ON_STACK) ? _alloca(sizeof(WSABUF) * (in_vec))
+ : malloc(sizeof(WSABUF) * (in_vec));
+ if (!pWsaBuf)
+ return APR_ENOMEM;
+
+ for (i = 0; i < in_vec; i++) {
+ pWsaBuf[i].buf = vec[i].iov_base;
+ pWsaBuf[i].len = (ULONG) vec[i].iov_len;
+ }
+#ifndef _WIN32_WCE
+ rv = WSASend(sock->socketdes, pWsaBuf, in_vec, &dwBytes, 0, NULL, NULL);
+ if (rv == SOCKET_ERROR) {
+ rc = apr_get_netos_error();
+ }
+#else
+ for (i = 0; i < in_vec; i++) {
+ rv = send(sock->socketdes, pWsaBuf[i].buf, pWsaBuf[i].len, 0);
+ if (rv == SOCKET_ERROR) {
+ rc = apr_get_netos_error();
+ break;
+ }
+ dwBytes += rv;
+ }
+#endif
+ if (in_vec > WSABUF_ON_STACK)
+ free(pWsaBuf);
+
+ *nbytes = dwBytes;
+ return rc;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_sendto(apr_socket_t *sock,
+ apr_sockaddr_t *where,
+ apr_int32_t flags, const char *buf,
+ apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ rv = sendto(sock->socketdes, buf, (int)*len, flags,
+ (const struct sockaddr*)&where->sa,
+ where->salen);
+ if (rv == SOCKET_ERROR) {
+ *len = 0;
+ return apr_get_netos_error();
+ }
+
+ *len = rv;
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_recvfrom(apr_sockaddr_t *from,
+ apr_socket_t *sock,
+ apr_int32_t flags,
+ char *buf, apr_size_t *len)
+{
+ apr_ssize_t rv;
+
+ from->salen = sizeof(from->sa);
+
+ rv = recvfrom(sock->socketdes, buf, (int)*len, flags,
+ (struct sockaddr*)&from->sa, &from->salen);
+ if (rv == SOCKET_ERROR) {
+ (*len) = 0;
+ return apr_get_netos_error();
+ }
+
+ apr_sockaddr_vars_set(from, from->sa.sin.sin_family,
+ ntohs(from->sa.sin.sin_port));
+
+ (*len) = rv;
+ if (rv == 0 && sock->type == SOCK_STREAM)
+ return APR_EOF;
+
+ return APR_SUCCESS;
+}
+
+
+#if APR_HAS_SENDFILE
+static apr_status_t collapse_iovec(char **off, apr_size_t *len,
+ struct iovec *iovec, int numvec,
+ char *buf, apr_size_t buflen)
+{
+ if (numvec == 1) {
+ *off = iovec[0].iov_base;
+ *len = iovec[0].iov_len;
+ }
+ else {
+ int i;
+ for (i = 0; i < numvec; i++) {
+ *len += iovec[i].iov_len;
+ }
+
+ if (*len > buflen) {
+ *len = 0;
+ return APR_INCOMPLETE;
+ }
+
+ *off = buf;
+
+ for (i = 0; i < numvec; i++) {
+ memcpy(buf, iovec[i].iov_base, iovec[i].iov_len);
+ buf += iovec[i].iov_len;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+
+/*
+ * apr_status_t apr_socket_sendfile(apr_socket_t *, apr_file_t *, apr_hdtr_t *,
+ * apr_off_t *, apr_size_t *, apr_int32_t flags)
+ * Send a file from an open file descriptor to a socket, along with
+ * optional headers and trailers
+ * arg 1) The socket to which we're writing
+ * arg 2) The open file from which to read
+ * arg 3) A structure containing the headers and trailers to send
+ * arg 4) Offset into the file where we should begin writing
+ * arg 5) Number of bytes to send out of the file
+ * arg 6) APR flags that are mapped to OS specific flags
+ */
+APR_DECLARE(apr_status_t) apr_socket_sendfile(apr_socket_t *sock,
+ apr_file_t *file,
+ apr_hdtr_t *hdtr,
+ apr_off_t *offset,
+ apr_size_t *len,
+ apr_int32_t flags)
+{
+ apr_status_t status = APR_SUCCESS;
+ apr_status_t rv;
+ apr_off_t curoff = *offset;
+ DWORD dwFlags = 0;
+ apr_size_t nbytes;
+ TRANSMIT_FILE_BUFFERS tfb, *ptfb = NULL;
+ apr_size_t bytes_to_send; /* Bytes to send out of the file (not including headers) */
+ int disconnected = 0;
+ int sendv_trailers = 0;
+ char hdtrbuf[4096];
+
+ if (apr_os_level < APR_WIN_NT) {
+ return APR_ENOTIMPL;
+ }
+
+ /* Use len to keep track of number of total bytes sent (including headers) */
+ bytes_to_send = *len;
+ *len = 0;
+
+ /* Handle the goofy case of sending headers/trailers and a zero byte file */
+ if (!bytes_to_send && hdtr) {
+ if (hdtr->numheaders) {
+ rv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders,
+ &nbytes);
+ if (rv != APR_SUCCESS)
+ return rv;
+ *len += nbytes;
+ }
+ if (hdtr->numtrailers) {
+ rv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers,
+ &nbytes);
+ if (rv != APR_SUCCESS)
+ return rv;
+ *len += nbytes;
+ }
+ return APR_SUCCESS;
+ }
+
+ memset(&tfb, '\0', sizeof (tfb));
+
+ /* Collapse the headers into a single buffer */
+ if (hdtr && hdtr->numheaders) {
+ apr_size_t head_length = tfb.HeadLength;
+ ptfb = &tfb;
+ nbytes = 0;
+ rv = collapse_iovec((char **)&ptfb->Head, &head_length,
+ hdtr->headers, hdtr->numheaders,
+ hdtrbuf, sizeof(hdtrbuf));
+
+ tfb.HeadLength = (DWORD)head_length;
+
+ /* If not enough buffer, punt to sendv */
+ if (rv == APR_INCOMPLETE) {
+ rv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders, &nbytes);
+ if (rv != APR_SUCCESS)
+ return rv;
+ *len += nbytes;
+ ptfb = NULL;
+ }
+ }
+
+ /* Initialize the overlapped structure used on TransmitFile
+ */
+ if (!sock->overlapped) {
+ sock->overlapped = apr_pcalloc(sock->pool, sizeof(OVERLAPPED));
+ sock->overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ }
+ while (bytes_to_send) {
+ DWORD xmitbytes;
+
+ if (bytes_to_send > MAX_SEGMENT_SIZE) {
+ xmitbytes = MAX_SEGMENT_SIZE;
+ }
+ else {
+ /* Last call to TransmitFile() */
+ xmitbytes = (DWORD)bytes_to_send;
+ /* Collapse the trailers into a single buffer */
+ if (hdtr && hdtr->numtrailers) {
+ apr_size_t tail_length = tfb.TailLength;
+ ptfb = &tfb;
+ rv = collapse_iovec((char**) &ptfb->Tail, &tail_length,
+ hdtr->trailers, hdtr->numtrailers,
+ hdtrbuf + ptfb->HeadLength,
+ sizeof(hdtrbuf) - ptfb->HeadLength);
+
+ tfb.TailLength = (DWORD)tail_length;
+
+ if (rv == APR_INCOMPLETE) {
+ /* If not enough buffer, punt to sendv, later */
+ sendv_trailers = 1;
+ }
+ }
+ /* Disconnect the socket after last send */
+ if ((flags & APR_SENDFILE_DISCONNECT_SOCKET)
+ && !sendv_trailers) {
+ dwFlags |= TF_REUSE_SOCKET;
+ dwFlags |= TF_DISCONNECT;
+ disconnected = 1;
+ }
+ }
+
+ sock->overlapped->Offset = (DWORD)(curoff);
+#if APR_HAS_LARGE_FILES
+ sock->overlapped->OffsetHigh = (DWORD)(curoff >> 32);
+#endif
+ /* XXX BoundsChecker claims dwFlags must not be zero. */
+ rv = TransmitFile(sock->socketdes, /* socket */
+ file->filehand, /* open file descriptor of the file to be sent */
+ xmitbytes, /* number of bytes to send. 0=send all */
+ 0, /* Number of bytes per send. 0=use default */
+ sock->overlapped, /* OVERLAPPED structure */
+ ptfb, /* header and trailer buffers */
+ dwFlags); /* flags to control various aspects of TransmitFile */
+ if (!rv) {
+ status = apr_get_netos_error();
+ if ((status == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) ||
+ (status == APR_FROM_OS_ERROR(WSA_IO_PENDING)))
+ {
+ rv = WaitForSingleObject(sock->overlapped->hEvent,
+ (DWORD)(sock->timeout >= 0
+ ? sock->timeout_ms : INFINITE));
+ if (rv == WAIT_OBJECT_0) {
+ status = APR_SUCCESS;
+ if (!disconnected) {
+ if (!WSAGetOverlappedResult(sock->socketdes,
+ sock->overlapped,
+ &xmitbytes,
+ FALSE,
+ &dwFlags)) {
+ status = apr_get_netos_error();
+ }
+ /* Ugly code alert: WSAGetOverlappedResult returns
+ * a count of all bytes sent. This loop only
+ * tracks bytes sent out of the file.
+ */
+ else if (ptfb) {
+ xmitbytes -= (ptfb->HeadLength + ptfb->TailLength);
+ }
+ }
+ }
+ else if (rv == WAIT_TIMEOUT) {
+ status = APR_FROM_OS_ERROR(WAIT_TIMEOUT);
+ }
+ else if (rv == WAIT_ABANDONED) {
+ /* Hummm... WAIT_ABANDONDED is not an error code. It is
+ * a return specific to the Win32 WAIT functions that
+ * indicates that a thread exited while holding a
+ * mutex. Should consider triggering an assert
+ * to detect the condition...
+ */
+ status = APR_FROM_OS_ERROR(WAIT_TIMEOUT);
+ }
+ else
+ status = apr_get_os_error();
+ }
+ }
+ if (status != APR_SUCCESS)
+ break;
+
+ bytes_to_send -= xmitbytes;
+ curoff += xmitbytes;
+ *len += xmitbytes;
+ /* Adjust len for any headers/trailers sent */
+ if (ptfb) {
+ *len += (ptfb->HeadLength + ptfb->TailLength);
+ memset(&tfb, '\0', sizeof (tfb));
+ ptfb = NULL;
+ }
+ }
+
+ if (status == APR_SUCCESS) {
+ if (sendv_trailers) {
+ rv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers, &nbytes);
+ if (rv != APR_SUCCESS)
+ return rv;
+ *len += nbytes;
+ }
+
+
+ /* Mark the socket as disconnected, but do not close it.
+ * Note: The application must have stored the socket prior to making
+ * the call to apr_socket_sendfile in order to either reuse it
+ * or close it.
+ */
+ if (disconnected) {
+ sock->disconnected = 1;
+ sock->socketdes = INVALID_SOCKET;
+ }
+ }
+
+ return status;
+}
+
+#endif
+
diff --git a/network_io/win32/sockets.c b/network_io/win32/sockets.c
new file mode 100644
index 0000000..5013bba
--- /dev/null
+++ b/network_io/win32/sockets.c
@@ -0,0 +1,559 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_network_io.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_portable.h"
+#include "apr_strings.h"
+#include <string.h>
+#include "apr_arch_inherit.h"
+#include "apr_arch_misc.h"
+
+/* Borrow the definition of SOMAXCONN_HINT() from Windows SDK 8,
+ * in case the SDK we are building against doesn't have it.
+ */
+#ifndef SOMAXCONN_HINT
+#define SOMAXCONN_HINT(b) (-(b))
+#endif
+
+static char generic_inaddr_any[16] = {0}; /* big enough for IPv4 or IPv6 */
+
+static apr_status_t socket_cleanup(void *sock)
+{
+ apr_socket_t *thesocket = sock;
+
+ if (thesocket->socketdes != INVALID_SOCKET) {
+ if (closesocket(thesocket->socketdes) == SOCKET_ERROR) {
+ return apr_get_netos_error();
+ }
+ thesocket->socketdes = INVALID_SOCKET;
+ }
+#if APR_HAS_SENDFILE
+ if (thesocket->overlapped) {
+ CloseHandle(thesocket->overlapped->hEvent);
+ thesocket->overlapped = NULL;
+ }
+#endif
+ return APR_SUCCESS;
+}
+
+static void set_socket_vars(apr_socket_t *sock, int family, int type, int protocol)
+{
+ sock->type = type;
+ sock->protocol = protocol;
+ apr_sockaddr_vars_set(sock->local_addr, family, 0);
+ apr_sockaddr_vars_set(sock->remote_addr, family, 0);
+#if APR_HAVE_IPV6
+ /* hard-coded behavior for older Windows IPv6 */
+ if (apr_os_level < APR_WIN_VISTA && family == AF_INET6) {
+ apr_set_option(sock, APR_IPV6_V6ONLY, 1);
+ }
+#endif
+}
+static void alloc_socket(apr_socket_t **new, apr_pool_t *p)
+{
+ *new = (apr_socket_t *)apr_pcalloc(p, sizeof(apr_socket_t));
+ (*new)->pool = p;
+ (*new)->local_addr = (apr_sockaddr_t *)apr_pcalloc((*new)->pool,
+ sizeof(apr_sockaddr_t));
+ (*new)->local_addr->pool = p;
+
+ (*new)->remote_addr = (apr_sockaddr_t *)apr_pcalloc((*new)->pool,
+ sizeof(apr_sockaddr_t));
+ (*new)->remote_addr->pool = p;
+ (*new)->remote_addr_unknown = 1;
+
+ /* Create a pollset with room for one descriptor. */
+ /* ### check return codes */
+ (void) apr_pollset_create(&(*new)->pollset, 1, p, 0);
+}
+
+APR_DECLARE(apr_status_t) apr_socket_protocol_get(apr_socket_t *sock,
+ int *protocol)
+{
+ *protocol = sock->protocol;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_create(apr_socket_t **new, int family,
+ int type, int protocol,
+ apr_pool_t *cont)
+{
+#if APR_HAVE_IPV6
+ int downgrade = (family == AF_UNSPEC);
+#endif
+
+ if (family == AF_UNSPEC) {
+#if APR_HAVE_IPV6
+ family = AF_INET6;
+#else
+ family = AF_INET;
+#endif
+ }
+
+ alloc_socket(new, cont);
+
+ /* For right now, we are not using socket groups. We may later.
+ * No flags to use when creating a socket, so use 0 for that parameter as well.
+ */
+ (*new)->socketdes = socket(family, type, protocol);
+#if APR_HAVE_IPV6
+ if ((*new)->socketdes == INVALID_SOCKET && downgrade) {
+ family = AF_INET;
+ (*new)->socketdes = socket(family, type, protocol);
+ }
+#endif
+
+ if ((*new)->socketdes == INVALID_SOCKET) {
+ return apr_get_netos_error();
+ }
+
+#ifdef WIN32
+ /* Socket handles are never truly inheritable, there are too many
+ * bugs associated. WSADuplicateSocket will copy them, but for our
+ * purposes, always transform the socket() created as a non-inherited
+ * handle
+ */
+#if APR_HAS_UNICODE_FS && !defined(_WIN32_WCE)
+ IF_WIN_OS_IS_UNICODE {
+ /* A different approach. Many users report errors such as
+ * (32538)An operation was attempted on something that is not
+ * a socket. : Parent: WSADuplicateSocket failed...
+ *
+ * This appears that the duplicated handle is no longer recognized
+ * as a socket handle. SetHandleInformation should overcome that
+ * problem by not altering the handle identifier. But this won't
+ * work on 9x - it's unsupported.
+ */
+ SetHandleInformation((HANDLE) (*new)->socketdes,
+ HANDLE_FLAG_INHERIT, 0);
+ }
+#if APR_HAS_ANSI_FS
+ /* only if APR_HAS_ANSI_FS && APR_HAS_UNICODE_FS */
+ ELSE_WIN_OS_IS_ANSI
+#endif
+#endif
+#if APR_HAS_ANSI_FS || defined(_WIN32_WCE)
+ {
+ HANDLE hProcess = GetCurrentProcess();
+ HANDLE dup;
+ if (DuplicateHandle(hProcess, (HANDLE) (*new)->socketdes, hProcess,
+ &dup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ closesocket((*new)->socketdes);
+ (*new)->socketdes = (SOCKET) dup;
+ }
+ }
+#endif
+
+#endif /* def WIN32 */
+
+ set_socket_vars(*new, family, type, protocol);
+
+ (*new)->timeout = -1;
+ (*new)->disconnected = 0;
+
+ apr_pool_cleanup_register((*new)->pool, (void *)(*new),
+ socket_cleanup, apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_shutdown(apr_socket_t *thesocket,
+ apr_shutdown_how_e how)
+{
+ int winhow = 0;
+
+#ifdef SD_RECEIVE
+ switch (how) {
+ case APR_SHUTDOWN_READ: {
+ winhow = SD_RECEIVE;
+ break;
+ }
+ case APR_SHUTDOWN_WRITE: {
+ winhow = SD_SEND;
+ break;
+ }
+ case APR_SHUTDOWN_READWRITE: {
+ winhow = SD_BOTH;
+ break;
+ }
+ default:
+ return APR_BADARG;
+ }
+#endif
+ if (shutdown(thesocket->socketdes, winhow) == 0) {
+ return APR_SUCCESS;
+ }
+ else {
+ return apr_get_netos_error();
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_socket_close(apr_socket_t *thesocket)
+{
+ apr_pool_cleanup_kill(thesocket->pool, thesocket, socket_cleanup);
+ return socket_cleanup(thesocket);
+}
+
+APR_DECLARE(apr_status_t) apr_socket_bind(apr_socket_t *sock,
+ apr_sockaddr_t *sa)
+{
+ if (bind(sock->socketdes,
+ (struct sockaddr *)&sa->sa,
+ sa->salen) == -1) {
+ return apr_get_netos_error();
+ }
+ else {
+ sock->local_addr = sa;
+ if (sock->local_addr->sa.sin.sin_port == 0) {
+ sock->local_port_unknown = 1; /* ephemeral port */
+ }
+ return APR_SUCCESS;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_socket_listen(apr_socket_t *sock,
+ apr_int32_t backlog)
+{
+ int backlog_val;
+
+ if (apr_os_level >= APR_WIN_8) {
+ /* Starting from Windows 8, listen() accepts a special SOMAXCONN_HINT()
+ * arg that allows setting the listen backlog value to a larger
+ * value than the predefined Winsock 2 limit (several hundred).
+ * https://blogs.msdn.microsoft.com/winsdk/2015/06/01/winsocks-listen-backlog-offers-more-flexibility-in-windows-8/
+ */
+ backlog_val = SOMAXCONN_HINT(backlog);
+ }
+ else {
+ backlog_val = backlog;
+ }
+
+ if (listen(sock->socketdes, backlog_val) == SOCKET_ERROR)
+ return apr_get_netos_error();
+ else
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_accept(apr_socket_t **new,
+ apr_socket_t *sock, apr_pool_t *p)
+{
+ SOCKET s;
+#if APR_HAVE_IPV6
+ struct sockaddr_storage sa;
+#else
+ struct sockaddr sa;
+#endif
+ int salen = sizeof(sock->remote_addr->sa);
+
+ /* Don't allocate the memory until after we call accept. This allows
+ us to work with nonblocking sockets. */
+ s = accept(sock->socketdes, (struct sockaddr *)&sa, &salen);
+ if (s == INVALID_SOCKET) {
+ return apr_get_netos_error();
+ }
+
+ alloc_socket(new, p);
+ set_socket_vars(*new, sock->local_addr->sa.sin.sin_family, SOCK_STREAM,
+ sock->protocol);
+
+ (*new)->timeout = -1;
+ (*new)->disconnected = 0;
+
+ (*new)->socketdes = s;
+ /* XXX next line looks bogus w.r.t. AF_INET6 support */
+ (*new)->remote_addr->salen = sizeof((*new)->remote_addr->sa);
+ memcpy (&(*new)->remote_addr->sa, &sa, salen);
+ *(*new)->local_addr = *sock->local_addr;
+ (*new)->remote_addr_unknown = 0;
+
+ /* The above assignment just overwrote the pool entry. Setting the local_addr
+ pool for the accepted socket back to what it should be. Otherwise all
+ allocations for this socket will come from a server pool that is not
+ freed until the process goes down.*/
+ (*new)->local_addr->pool = p;
+
+ /* fix up any pointers which are no longer valid */
+ if (sock->local_addr->sa.sin.sin_family == AF_INET) {
+ (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin.sin_addr;
+ }
+#if APR_HAVE_IPV6
+ else if (sock->local_addr->sa.sin.sin_family == AF_INET6) {
+ (*new)->local_addr->ipaddr_ptr = &(*new)->local_addr->sa.sin6.sin6_addr;
+ }
+#endif
+ (*new)->remote_addr->port = ntohs((*new)->remote_addr->sa.sin.sin_port);
+ if (sock->local_port_unknown) {
+ /* not likely for a listening socket, but theoretically possible :) */
+ (*new)->local_port_unknown = 1;
+ }
+
+#if APR_TCP_NODELAY_INHERITED
+ if (apr_is_option_set(sock, APR_TCP_NODELAY) == 1) {
+ apr_set_option(*new, APR_TCP_NODELAY, 1);
+ }
+#endif /* TCP_NODELAY_INHERITED */
+#if APR_O_NONBLOCK_INHERITED
+ if (apr_is_option_set(sock, APR_SO_NONBLOCK) == 1) {
+ apr_set_option(*new, APR_SO_NONBLOCK, 1);
+ }
+#endif /* APR_O_NONBLOCK_INHERITED */
+
+ if (sock->local_interface_unknown ||
+ !memcmp(sock->local_addr->ipaddr_ptr,
+ generic_inaddr_any,
+ sock->local_addr->ipaddr_len)) {
+ /* If the interface address inside the listening socket's local_addr wasn't
+ * up-to-date, we don't know local interface of the connected socket either.
+ *
+ * If the listening socket was not bound to a specific interface, we
+ * don't know the local_addr of the connected socket.
+ */
+ (*new)->local_interface_unknown = 1;
+ }
+
+ apr_pool_cleanup_register((*new)->pool, (void *)(*new),
+ socket_cleanup, apr_pool_cleanup_null);
+ return APR_SUCCESS;
+}
+
+static apr_status_t wait_for_connect(apr_socket_t *sock)
+{
+ int rc;
+ struct timeval tv, *tvptr;
+ fd_set wfdset, efdset;
+
+ /* wait for the connect to complete or timeout */
+ FD_ZERO(&wfdset);
+ FD_SET(sock->socketdes, &wfdset);
+ FD_ZERO(&efdset);
+ FD_SET(sock->socketdes, &efdset);
+
+ if (sock->timeout < 0) {
+ tvptr = NULL;
+ }
+ else {
+ /* casts for winsock/timeval definition */
+ tv.tv_sec = (long)apr_time_sec(sock->timeout);
+ tv.tv_usec = (int)apr_time_usec(sock->timeout);
+ tvptr = &tv;
+ }
+ rc = select(FD_SETSIZE+1, NULL, &wfdset, &efdset, tvptr);
+ if (rc == SOCKET_ERROR) {
+ return apr_get_netos_error();
+ }
+ else if (!rc) {
+ return APR_FROM_OS_ERROR(WSAETIMEDOUT);
+ }
+ /* Evaluate the efdset */
+ if (FD_ISSET(sock->socketdes, &efdset)) {
+ /* The connect failed. */
+ int rclen = sizeof(rc);
+ if (getsockopt(sock->socketdes, SOL_SOCKET, SO_ERROR, (char*) &rc, &rclen)) {
+ return apr_get_netos_error();
+ }
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_connect(apr_socket_t *sock,
+ apr_sockaddr_t *sa)
+{
+ apr_status_t rv;
+
+ if ((sock->socketdes == INVALID_SOCKET) || (!sock->local_addr)) {
+ return APR_ENOTSOCK;
+ }
+
+ if (connect(sock->socketdes, (const struct sockaddr *)&sa->sa.sin,
+ sa->salen) == SOCKET_ERROR) {
+ rv = apr_get_netos_error();
+ }
+ else {
+ rv = APR_SUCCESS;
+ }
+
+ if (rv == APR_FROM_OS_ERROR(WSAEWOULDBLOCK)) {
+ if (sock->timeout == 0) {
+ /* Tell the app that the connect is in progress...
+ * Gotta play some games here. connect on Unix will return
+ * EINPROGRESS under the same circumstances that Windows
+ * returns WSAEWOULDBLOCK. Do some adhoc canonicalization...
+ */
+ rv = APR_FROM_OS_ERROR(WSAEINPROGRESS);
+ }
+ else {
+ rv = wait_for_connect(sock);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+ }
+
+ if (memcmp(sa->ipaddr_ptr, generic_inaddr_any, sa->ipaddr_len)) {
+ /* A real remote address was passed in. If the unspecified
+ * address was used, the actual remote addr will have to be
+ * determined using getpeername() if required. */
+ sock->remote_addr_unknown = 0;
+
+ /* Copy the address structure details in. */
+ sock->remote_addr = sa;
+ }
+
+ if (sock->local_addr->sa.sin.sin_port == 0) {
+ /* connect() got us an ephemeral port */
+ sock->local_port_unknown = 1;
+ }
+ if (!memcmp(sock->local_addr->ipaddr_ptr,
+ generic_inaddr_any,
+ sock->local_addr->ipaddr_len)) {
+ /* not bound to specific local interface; connect() had to assign
+ * one for the socket
+ */
+ sock->local_interface_unknown = 1;
+ }
+
+ if (rv != APR_SUCCESS && rv != APR_FROM_OS_ERROR(WSAEISCONN)) {
+ return rv;
+ }
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_type_get(apr_socket_t *sock, int *type)
+{
+ *type = sock->type;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_data_get(void **data, const char *key,
+ apr_socket_t *sock)
+{
+ sock_userdata_t *cur = sock->userdata;
+
+ *data = NULL;
+
+ while (cur) {
+ if (!strcmp(cur->key, key)) {
+ *data = cur->data;
+ break;
+ }
+ cur = cur->next;
+ }
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_data_set(apr_socket_t *sock, void *data,
+ const char *key,
+ apr_status_t (*cleanup)(void *))
+{
+ sock_userdata_t *new = apr_palloc(sock->pool, sizeof(sock_userdata_t));
+
+ new->key = apr_pstrdup(sock->pool, key);
+ new->data = data;
+ new->next = sock->userdata;
+ sock->userdata = new;
+
+ if (cleanup) {
+ apr_pool_cleanup_register(sock->pool, data, cleanup, cleanup);
+ }
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_os_sock_get(apr_os_sock_t *thesock,
+ apr_socket_t *sock)
+{
+ *thesock = sock->socketdes;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_os_sock_make(apr_socket_t **apr_sock,
+ apr_os_sock_info_t *os_sock_info,
+ apr_pool_t *cont)
+{
+ alloc_socket(apr_sock, cont);
+ set_socket_vars(*apr_sock, os_sock_info->family, os_sock_info->type, os_sock_info->protocol);
+ (*apr_sock)->timeout = -1;
+ (*apr_sock)->disconnected = 0;
+ (*apr_sock)->socketdes = *os_sock_info->os_sock;
+ if (os_sock_info->local) {
+ memcpy(&(*apr_sock)->local_addr->sa.sin,
+ os_sock_info->local,
+ (*apr_sock)->local_addr->salen);
+ (*apr_sock)->local_addr->pool = cont;
+ /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */
+ (*apr_sock)->local_addr->port = ntohs((*apr_sock)->local_addr->sa.sin.sin_port);
+ }
+ else {
+ (*apr_sock)->local_port_unknown = (*apr_sock)->local_interface_unknown = 1;
+ }
+ if (os_sock_info->remote) {
+ memcpy(&(*apr_sock)->remote_addr->sa.sin,
+ os_sock_info->remote,
+ (*apr_sock)->remote_addr->salen);
+ (*apr_sock)->remote_addr->pool = cont;
+ /* XXX IPv6 - this assumes sin_port and sin6_port at same offset */
+ (*apr_sock)->remote_addr->port = ntohs((*apr_sock)->remote_addr->sa.sin.sin_port);
+ (*apr_sock)->remote_addr_unknown = 0;
+ }
+
+ apr_pool_cleanup_register((*apr_sock)->pool, (void *)(*apr_sock),
+ socket_cleanup, apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_os_sock_put(apr_socket_t **sock,
+ apr_os_sock_t *thesock,
+ apr_pool_t *cont)
+{
+ if ((*sock) == NULL) {
+ alloc_socket(sock, cont);
+ /* XXX figure out the actual socket type here */
+ /* *or* just decide that apr_os_sock_put() has to be told the family and type */
+ set_socket_vars(*sock, AF_INET, SOCK_STREAM, 0);
+ (*sock)->timeout = -1;
+ (*sock)->disconnected = 0;
+ }
+ (*sock)->local_port_unknown = (*sock)->local_interface_unknown = 1;
+ (*sock)->remote_addr_unknown = 1;
+ (*sock)->socketdes = *thesock;
+ return APR_SUCCESS;
+}
+
+
+/* Sockets cannot be inherited through the standard sockets
+ * inheritence. WSADuplicateSocket must be used.
+ * This is not trivial to implement.
+ */
+
+APR_DECLARE(apr_status_t) apr_socket_inherit_set(apr_socket_t *socket)
+{
+ return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_socket_inherit_unset(apr_socket_t *socket)
+{
+ return APR_ENOTIMPL;
+}
+
+APR_POOL_IMPLEMENT_ACCESSOR(socket);
diff --git a/network_io/win32/sockopt.c b/network_io/win32/sockopt.c
new file mode 100644
index 0000000..463eeeb
--- /dev/null
+++ b/network_io/win32/sockopt.c
@@ -0,0 +1,302 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_networkio.h"
+#include "apr_arch_misc.h" /* apr_os_level */
+#include "apr_network_io.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include <string.h>
+
+/* IPV6_V6ONLY is missing from pre-Windows 2008 SDK as well as MinGW
+ * (at least up through 1.0.16).
+ * Runtime support is a separate issue.
+ */
+#ifndef IPV6_V6ONLY
+#define IPV6_V6ONLY 27
+#endif
+
+static apr_status_t soblock(SOCKET sd)
+{
+ u_long zero = 0;
+
+ if (ioctlsocket(sd, FIONBIO, &zero) == SOCKET_ERROR) {
+ return apr_get_netos_error();
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t sononblock(SOCKET sd)
+{
+ u_long one = 1;
+
+ if (ioctlsocket(sd, FIONBIO, &one) == SOCKET_ERROR) {
+ return apr_get_netos_error();
+ }
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
+{
+ apr_status_t stat;
+
+ if (t == 0) {
+ /* Set the socket non-blocking if it was previously blocking */
+ if (sock->timeout != 0) {
+ if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS)
+ return stat;
+ }
+ }
+ else if (t > 0) {
+ /* Set the socket to blocking if it was previously non-blocking */
+ if (sock->timeout == 0 || apr_is_option_set(sock, APR_SO_NONBLOCK)) {
+ if ((stat = soblock(sock->socketdes)) != APR_SUCCESS)
+ return stat;
+ apr_set_option(sock, APR_SO_NONBLOCK, 0);
+ }
+ /* Reset socket timeouts if the new timeout differs from the old timeout */
+ if (sock->timeout != t)
+ {
+ /* Win32 timeouts are in msec, represented as int */
+ sock->timeout_ms = (int)apr_time_as_msec(t);
+ setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVTIMEO,
+ (char *) &sock->timeout_ms,
+ sizeof(sock->timeout_ms));
+ setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDTIMEO,
+ (char *) &sock->timeout_ms,
+ sizeof(sock->timeout_ms));
+ }
+ }
+ else if (t < 0) {
+ int zero = 0;
+ /* Set the socket to blocking with infinite timeouts */
+ if ((stat = soblock(sock->socketdes)) != APR_SUCCESS)
+ return stat;
+ setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVTIMEO,
+ (char *) &zero, sizeof(zero));
+ setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDTIMEO,
+ (char *) &zero, sizeof(zero));
+ }
+ sock->timeout = t;
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_opt_set(apr_socket_t *sock,
+ apr_int32_t opt, apr_int32_t on)
+{
+ int one;
+ apr_status_t stat;
+
+ one = on ? 1 : 0;
+
+ switch (opt) {
+ case APR_SO_KEEPALIVE:
+ if (on != apr_is_option_set(sock, APR_SO_KEEPALIVE)) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_KEEPALIVE,
+ (void *)&one, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ apr_set_option(sock, APR_SO_KEEPALIVE, on);
+ }
+ break;
+ case APR_SO_DEBUG:
+ if (on != apr_is_option_set(sock, APR_SO_DEBUG)) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_DEBUG,
+ (void *)&one, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ apr_set_option(sock, APR_SO_DEBUG, on);
+ }
+ break;
+ case APR_SO_SNDBUF:
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDBUF,
+ (void *)&on, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ break;
+ case APR_SO_RCVBUF:
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVBUF,
+ (void *)&on, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ break;
+ case APR_SO_BROADCAST:
+ if (on != apr_is_option_set(sock, APR_SO_BROADCAST)) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_BROADCAST,
+ (void *)&one, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ apr_set_option(sock, APR_SO_BROADCAST, on);
+ }
+ break;
+ case APR_SO_REUSEADDR:
+ if (on != apr_is_option_set(sock, APR_SO_REUSEADDR)) {
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&one, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ apr_set_option(sock, APR_SO_REUSEADDR, on);
+ }
+ break;
+ case APR_SO_NONBLOCK:
+ if (apr_is_option_set(sock, APR_SO_NONBLOCK) != on) {
+ if (on) {
+ if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS)
+ return stat;
+ }
+ else {
+ if ((stat = soblock(sock->socketdes)) != APR_SUCCESS)
+ return stat;
+ }
+ apr_set_option(sock, APR_SO_NONBLOCK, on);
+ }
+ break;
+ case APR_SO_LINGER:
+ {
+ if (apr_is_option_set(sock, APR_SO_LINGER) != on) {
+ struct linger li;
+ li.l_onoff = on;
+ li.l_linger = APR_MAX_SECS_TO_LINGER;
+ if (setsockopt(sock->socketdes, SOL_SOCKET, SO_LINGER,
+ (char *) &li, sizeof(struct linger)) == -1) {
+ return apr_get_netos_error();
+ }
+ apr_set_option(sock, APR_SO_LINGER, on);
+ }
+ break;
+ }
+ case APR_TCP_DEFER_ACCEPT:
+#if defined(TCP_DEFER_ACCEPT)
+ if (apr_is_option_set(sock, APR_TCP_DEFER_ACCEPT) != on) {
+ int optlevel = IPPROTO_TCP;
+ int optname = TCP_DEFER_ACCEPT;
+
+ if (setsockopt(sock->socketdes, optlevel, optname,
+ (void *)&on, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ apr_set_option(sock, APR_TCP_DEFER_ACCEPT, on);
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+ case APR_TCP_NODELAY:
+ if (apr_is_option_set(sock, APR_TCP_NODELAY) != on) {
+ int optlevel = IPPROTO_TCP;
+ int optname = TCP_NODELAY;
+
+#if APR_HAVE_SCTP
+ if (sock->protocol == IPPROTO_SCTP) {
+ optlevel = IPPROTO_SCTP;
+ optname = SCTP_NODELAY;
+ }
+#endif
+ if (setsockopt(sock->socketdes, optlevel, optname,
+ (void *)&on, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ apr_set_option(sock, APR_TCP_NODELAY, on);
+ }
+ break;
+ case APR_IPV6_V6ONLY:
+#if APR_HAVE_IPV6
+ if (apr_os_level < APR_WIN_VISTA &&
+ sock->local_addr->family == AF_INET6) {
+ /* apr_set_option() called at socket creation */
+ if (on) {
+ return APR_SUCCESS;
+ }
+ else {
+ return APR_ENOTIMPL;
+ }
+ }
+ /* we don't know the initial setting of this option,
+ * so don't check sock->options since that optimization
+ * won't work
+ */
+ if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY,
+ (void *)&on, sizeof(int)) == -1) {
+ return apr_get_netos_error();
+ }
+ apr_set_option(sock, APR_IPV6_V6ONLY, on);
+#else
+ return APR_ENOTIMPL;
+#endif
+ break;
+ default:
+ return APR_EINVAL;
+ break;
+ }
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
+{
+ *t = sock->timeout;
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_opt_get(apr_socket_t *sock,
+ apr_int32_t opt, apr_int32_t *on)
+{
+ switch (opt) {
+ case APR_SO_DISCONNECTED:
+ *on = sock->disconnected;
+ break;
+ case APR_SO_KEEPALIVE:
+ case APR_SO_DEBUG:
+ case APR_SO_REUSEADDR:
+ case APR_SO_NONBLOCK:
+ case APR_SO_LINGER:
+ default:
+ *on = apr_is_option_set(sock, opt);
+ break;
+ }
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_socket_atmark(apr_socket_t *sock, int *atmark)
+{
+ u_long oobmark;
+
+ if (ioctlsocket(sock->socketdes, SIOCATMARK, (void*) &oobmark) < 0)
+ return apr_get_netos_error();
+
+ *atmark = (oobmark != 0);
+
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_gethostname(char *buf, int len,
+ apr_pool_t *cont)
+{
+ if (gethostname(buf, len) == -1) {
+ buf[0] = '\0';
+ return apr_get_netos_error();
+ }
+ else if (!memchr(buf, '\0', len)) { /* buffer too small */
+ buf[0] = '\0';
+ return APR_ENAMETOOLONG;
+ }
+ return APR_SUCCESS;
+}
+