summaryrefslogtreecommitdiffstats
path: root/lib/system/sockets.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system/sockets.c')
-rw-r--r--lib/system/sockets.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/lib/system/sockets.c b/lib/system/sockets.c
new file mode 100644
index 0000000..e1d6fd1
--- /dev/null
+++ b/lib/system/sockets.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2010-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <system.h>
+#include "gnutls_int.h"
+#include "errors.h"
+
+#include <sys/socket.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+# include <windows.h>
+#else /* !_WIN32 */
+# include <poll.h>
+#endif
+
+/* System specific socket function wrappers.
+ */
+
+#ifdef _WIN32
+/* Do not use the gnulib functions for sending and receiving data.
+ * Using them makes gnutls only working with gnulib applications.
+ */
+#undef send
+#undef recv
+#undef select
+
+int system_errno(gnutls_transport_ptr p)
+{
+ int tmperr = WSAGetLastError();
+ int ret = 0;
+ switch (tmperr) {
+ case WSAEWOULDBLOCK:
+ ret = EAGAIN;
+ break;
+ case NO_ERROR:
+ ret = 0;
+ break;
+ case WSAEINTR:
+ ret = EINTR;
+ break;
+ case WSAEMSGSIZE:
+ ret = EMSGSIZE;
+ break;
+ default:
+ ret = EIO;
+ break;
+ }
+ WSASetLastError(tmperr);
+
+ return ret;
+}
+
+ssize_t
+system_write(gnutls_transport_ptr ptr, const void *data, size_t data_size)
+{
+ return send(GNUTLS_POINTER_TO_INT(ptr), data, data_size, 0);
+}
+
+ssize_t
+system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
+ int iovec_cnt)
+{
+ WSABUF bufs[32];
+ DWORD bytes_sent;
+ DWORD to_send_cnt = 0;
+ size_t to_send_bytes = 0;
+
+ if ((size_t)iovec_cnt > sizeof(bufs) / sizeof(bufs[0]))
+ iovec_cnt = sizeof(bufs) / sizeof(bufs[0]);
+
+ while (to_send_cnt < (DWORD)iovec_cnt && to_send_bytes < SSIZE_MAX) {
+ bufs[to_send_cnt].buf = iovec[to_send_cnt].iov_base;
+
+ if (to_send_bytes + iovec[to_send_cnt].iov_len > SSIZE_MAX) {
+ /* Return value limit: successful result value cannot
+ * exceed SSIZE_MAX */
+ size_t space_left = (size_t)SSIZE_MAX - to_send_bytes;
+ bufs[to_send_cnt].len = (unsigned long)
+ (space_left > ULONG_MAX ?
+ ULONG_MAX : space_left);
+ to_send_cnt++;
+ break;
+ }
+#ifdef _WIN64
+ if (iovec[to_send_cnt].iov_len > ULONG_MAX) {
+ /* WSASend() limitation */
+ bufs[to_send_cnt].len = ULONG_MAX;
+ to_send_cnt++;
+ break;
+ }
+#endif
+ bufs[to_send_cnt].len =
+ (unsigned long) iovec[to_send_cnt].iov_len;
+ to_send_bytes += iovec[to_send_cnt].iov_len;
+ to_send_cnt++;
+ }
+
+ if (WSASend(GNUTLS_POINTER_TO_INT(ptr), bufs, to_send_cnt, &bytes_sent,
+ 0, NULL, NULL) != 0)
+ return -1;
+
+ return (ssize_t)bytes_sent;
+}
+
+#else /* POSIX */
+int system_errno(gnutls_transport_ptr_t ptr)
+{
+#if defined(_AIX) || defined(AIX)
+ if (errno == 0)
+ errno = EAGAIN;
+#endif
+
+ return errno;
+}
+
+static ssize_t
+_system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
+ int iovec_cnt, int flags)
+{
+ struct msghdr hdr;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.msg_iov = (struct iovec *)iovec;
+ hdr.msg_iovlen = iovec_cnt;
+
+ return sendmsg(GNUTLS_POINTER_TO_INT(ptr), &hdr, flags);
+}
+
+#ifdef MSG_NOSIGNAL
+ssize_t
+system_writev_nosignal(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
+ int iovec_cnt)
+{
+ return _system_writev(ptr, iovec, iovec_cnt, MSG_NOSIGNAL);
+}
+
+#endif
+
+ssize_t
+system_writev(gnutls_transport_ptr_t ptr, const giovec_t * iovec,
+ int iovec_cnt)
+{
+ return _system_writev(ptr, iovec, iovec_cnt, 0);
+}
+
+#endif
+
+
+ssize_t
+system_read(gnutls_transport_ptr_t ptr, void *data, size_t data_size)
+{
+ return recv(GNUTLS_POINTER_TO_INT(ptr), data, data_size, 0);
+}
+
+/**
+ * gnutls_system_recv_timeout:
+ * @ptr: A file descriptor (wrapped in a gnutls_transport_ptr_t pointer)
+ * @ms: The number of milliseconds to wait.
+ *
+ * Wait for data to be received from the provided socket (@ptr) within a
+ * timeout period in milliseconds, using select() on the provided @ptr.
+ *
+ * This function is provided as a helper for constructing custom
+ * callbacks for gnutls_transport_set_pull_timeout_function(),
+ * which can be used if you rely on socket file descriptors.
+ *
+ * Returns -1 on error, 0 on timeout, positive value if data are available for reading.
+ *
+ * Since: 3.4.0
+ **/
+int gnutls_system_recv_timeout(gnutls_transport_ptr_t ptr, unsigned int ms)
+{
+ int ret;
+ int fd = GNUTLS_POINTER_TO_INT(ptr);
+#ifndef _WIN32
+ int timeo;
+ struct pollfd pfd;
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+
+ if (ms == GNUTLS_INDEFINITE_TIMEOUT)
+ timeo = -1;
+ else
+ timeo = ms;
+ do {
+ ret = poll(&pfd, 1, timeo);
+ } while(ret == -1 && errno == EINTR);
+#else
+ fd_set rfds;
+ struct timeval _tv, *tv = NULL;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ if (ms != GNUTLS_INDEFINITE_TIMEOUT) {
+ _tv.tv_sec = ms/1000;
+ _tv.tv_usec = (ms % 1000) * 1000;
+ tv = &_tv;
+ }
+
+ ret = select(fd + 1, &rfds, NULL, NULL, tv);
+#endif
+ if (ret <= 0)
+ return ret;
+
+ return ret;
+}
+