summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_socket.c')
-rw-r--r--src/nxt_socket.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/nxt_socket.c b/src/nxt_socket.c
new file mode 100644
index 00000000..19ba21c0
--- /dev/null
+++ b/src/nxt_socket.c
@@ -0,0 +1,317 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+
+static const char *nxt_socket_sockopt_name(nxt_uint_t level,
+ nxt_uint_t sockopt);
+
+
+nxt_socket_t
+nxt_socket_create(nxt_uint_t domain, nxt_uint_t type, nxt_uint_t protocol,
+ nxt_uint_t flags)
+{
+ nxt_socket_t s;
+
+#if (NXT_HAVE_SOCK_NONBLOCK)
+
+ if (flags & NXT_NONBLOCK) {
+ type |= SOCK_NONBLOCK;
+ }
+
+#endif
+
+ s = socket(domain, type, protocol);
+
+ if (nxt_slow_path(s == -1)) {
+ nxt_thread_log_alert("socket(%ui, 0x%uXi, %ui) failed %E",
+ domain, type, protocol, nxt_socket_errno);
+ return s;
+ }
+
+ nxt_thread_log_debug("socket(): %d", s);
+
+#if !(NXT_HAVE_SOCK_NONBLOCK)
+
+ if (flags & NXT_NONBLOCK) {
+ if (nxt_slow_path(nxt_socket_nonblocking(s) != NXT_OK)) {
+ nxt_socket_close(s);
+ return -1;
+ }
+ }
+
+#endif
+
+ return s;
+}
+
+
+void
+nxt_socket_close(nxt_socket_t s)
+{
+ if (nxt_fast_path(close(s) == 0)) {
+ nxt_thread_log_debug("socket close(%d)", s);
+
+ } else {
+ nxt_thread_log_alert("socket close(%d) failed %E", s, nxt_socket_errno);
+ }
+}
+
+
+nxt_int_t
+nxt_socket_getsockopt(nxt_socket_t s, nxt_uint_t level, nxt_uint_t sockopt)
+{
+ int val;
+ socklen_t len;
+
+ len = sizeof(val);
+
+ if (nxt_fast_path(getsockopt(s, level, sockopt, &val, &len) == 0)) {
+ nxt_thread_log_debug("getsockopt(%d, %ui, %s): %d",
+ s, level,
+ nxt_socket_sockopt_name(level, sockopt), val);
+ return val;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT, "getsockopt(%d, %ui, %s) failed %E",
+ s, level, nxt_socket_sockopt_name(level, sockopt),
+ val, nxt_socket_errno);
+
+ return -1;
+}
+
+
+nxt_int_t
+nxt_socket_setsockopt(nxt_socket_t s, nxt_uint_t level, nxt_uint_t sockopt,
+ int val)
+{
+ socklen_t len;
+
+ len = sizeof(val);
+
+ if (nxt_fast_path(setsockopt(s, level, sockopt, &val, len) == 0)) {
+ nxt_thread_log_debug("setsockopt(%d, %ui, %s): %d",
+ s, level,
+ nxt_socket_sockopt_name(level, sockopt), val);
+ return NXT_OK;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT, "setsockopt(%d, %ui, %s, %d) failed %E",
+ s, level, nxt_socket_sockopt_name(level, sockopt),
+ val, nxt_socket_errno);
+
+ return NXT_ERROR;
+}
+
+
+static const char *
+nxt_socket_sockopt_name(nxt_uint_t level, nxt_uint_t sockopt)
+{
+ switch (level) {
+
+ case SOL_SOCKET:
+ switch (sockopt) {
+
+ case SO_SNDBUF:
+ return "SO_SNDBUF";
+
+ case SO_RCVBUF:
+ return "SO_RCVBUF";
+
+ case SO_REUSEADDR:
+ return "SO_TYPE";
+
+ case SO_TYPE:
+ return "SO_TYPE";
+ }
+
+ break;
+
+ case IPPROTO_TCP:
+ switch (sockopt) {
+
+ case TCP_NODELAY:
+ return "TCP_NODELAY";
+
+#ifdef TCP_DEFER_ACCEPT
+ case TCP_DEFER_ACCEPT:
+ return "TCP_DEFER_ACCEPT";
+#endif
+ }
+
+ break;
+
+#if (NXT_INET6 && defined IPV6_V6ONLY)
+ case IPPROTO_IPV6:
+
+ switch (sockopt) {
+
+ case IPV6_V6ONLY:
+ return "IPV6_V6ONLY";
+ }
+
+ break;
+#endif
+
+ }
+
+ return "";
+}
+
+
+nxt_int_t
+nxt_socket_bind(nxt_socket_t s, nxt_sockaddr_t *sa, nxt_bool_t test)
+{
+ nxt_err_t err;
+
+ nxt_thread_log_debug("bind(%d, %*s)", s, sa->text_len, sa->text);
+
+ if (nxt_fast_path(bind(s, &sa->u.sockaddr, nxt_socklen(sa)) == 0)) {
+ return NXT_OK;
+ }
+
+ err = nxt_socket_errno;
+
+ if (err == NXT_EADDRINUSE && test) {
+ return NXT_DECLINED;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT, "bind(%d, %*s) failed %E",
+ s, sa->text_len, sa->text, err);
+
+ return NXT_ERROR;
+}
+
+
+nxt_int_t
+nxt_socket_connect(nxt_socket_t s, nxt_sockaddr_t *sa)
+{
+ nxt_err_t err;
+ nxt_int_t ret;
+ nxt_uint_t level;
+
+ nxt_thread_log_debug("connect(%d, %*s)", s, sa->text_len, sa->text);
+
+ if (connect(s, &sa->u.sockaddr, nxt_socklen(sa)) == 0) {
+ return NXT_OK;
+ }
+
+ err = nxt_socket_errno;
+
+ switch (err) {
+
+ case NXT_EINPROGRESS:
+ nxt_thread_log_debug("connect(%d) in progress", s);
+ return NXT_AGAIN;
+
+ case NXT_ECONNREFUSED:
+#if (NXT_LINUX)
+ case NXT_EAGAIN:
+ /*
+ * Linux returns EAGAIN instead of ECONNREFUSED
+ * for UNIX sockets if a listen queue is full.
+ */
+#endif
+ level = NXT_LOG_ERR;
+ ret = NXT_DECLINED;
+ break;
+
+ case NXT_ECONNRESET:
+ case NXT_ENETDOWN:
+ case NXT_ENETUNREACH:
+ case NXT_EHOSTDOWN:
+ case NXT_EHOSTUNREACH:
+ level = NXT_LOG_ERR;
+ ret = NXT_ERROR;
+ break;
+
+ default:
+ level = NXT_LOG_CRIT;
+ ret = NXT_ERROR;
+ }
+
+ nxt_thread_log_error(level, "connect(%d, %*s) failed %E",
+ s, sa->text_len, sa->text, err);
+
+ return ret;
+}
+
+
+void
+nxt_socket_shutdown(nxt_socket_t s, nxt_uint_t how)
+{
+ nxt_err_t err;
+ nxt_uint_t level;
+
+ if (nxt_fast_path(shutdown(s, how) == 0)) {
+ nxt_thread_log_debug("shutdown(%d, %ui)", s, how);
+ return;
+ }
+
+ err = nxt_socket_errno;
+
+ switch (err) {
+
+ case NXT_ENOTCONN:
+ level = NXT_LOG_INFO;
+ break;
+
+ case NXT_ECONNRESET:
+ case NXT_ENETDOWN:
+ case NXT_ENETUNREACH:
+ case NXT_EHOSTDOWN:
+ case NXT_EHOSTUNREACH:
+ level = NXT_LOG_ERR;
+ break;
+
+ default:
+ level = NXT_LOG_CRIT;
+ }
+
+ nxt_thread_log_error(level, "shutdown(%d, %ui) failed %E", s, how, err);
+}
+
+
+nxt_uint_t
+nxt_socket_error_level(nxt_err_t err, nxt_socket_error_level_t level)
+{
+ switch (err) {
+
+ case NXT_ECONNRESET:
+
+ if ((level & NXT_SOCKET_ECONNRESET_IGNORE) != 0) {
+ return NXT_LOG_DEBUG;
+ }
+
+ return NXT_LOG_ERR;
+
+ case NXT_EINVAL:
+
+ if ((level & NXT_SOCKET_EINVAL_IGNORE) != 0) {
+ return NXT_LOG_DEBUG;
+ }
+
+ return NXT_LOG_ALERT;
+
+ case NXT_EPIPE:
+ case NXT_ENOTCONN:
+ case NXT_ETIMEDOUT:
+ case NXT_ENETDOWN:
+ case NXT_ENETUNREACH:
+ case NXT_EHOSTDOWN:
+ case NXT_EHOSTUNREACH:
+
+ if ((level & NXT_SOCKET_ERROR_IGNORE) != 0) {
+ return NXT_LOG_INFO;
+ }
+
+ return NXT_LOG_ERR;
+
+ default:
+ return NXT_LOG_ALERT;
+ }
+}