/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static u_char *nxt_listen_socket_log_handler(void *ctx, u_char *pos, u_char *last); nxt_int_t nxt_listen_socket(nxt_task_t *task, nxt_socket_t s, int backlog) { nxt_debug(task, "listen(%d, %d)", s, backlog); if (nxt_fast_path(listen(s, backlog) == 0)) { return NXT_OK; } nxt_alert(task, "listen(%d, %d) failed %E", s, backlog, nxt_socket_errno); return NXT_ERROR; } nxt_int_t nxt_listen_socket_create(nxt_task_t *task, nxt_mp_t *mp, nxt_listen_socket_t *ls) { nxt_log_t log, *old; nxt_uint_t family; nxt_socket_t s; nxt_thread_t *thr; nxt_sockaddr_t *sa; #if (NXT_HAVE_UNIX_DOMAIN) int ret; u_char *p; nxt_err_t err; nxt_socket_t ts; nxt_sockaddr_t *orig_sa; nxt_file_name_t *name, *tmp; nxt_file_access_t access; #endif sa = ls->sockaddr; thr = nxt_thread(); old = thr->log; log = *thr->log; log.ctx_handler = nxt_listen_socket_log_handler; log.ctx = sa; thr->log = &log; family = sa->u.sockaddr.sa_family; s = nxt_socket_create(task, family, sa->type, 0, ls->flags); if (s == -1) { goto fail; } if (nxt_socket_setsockopt(task, s, SOL_SOCKET, SO_REUSEADDR, 1) != NXT_OK) { goto fail; } #if (NXT_INET6 && defined IPV6_V6ONLY) if (family == AF_INET6 && ls->ipv6only) { int ipv6only; ipv6only = (ls->ipv6only == 1); /* Ignore possible error. TODO: why? */ (void) nxt_socket_setsockopt(task, s, IPPROTO_IPV6, IPV6_V6ONLY, ipv6only); } #endif #if 0 /* Ignore possible error. TODO: why? */ (void) nxt_socket_setsockopt(task, s, SOL_SOCKET, SO_SNDBUF, 8192); #endif if (ls->read_after_accept) { nxt_socket_defer_accept(task, s, sa); } #if (NXT_HAVE_UNIX_DOMAIN) if (family == AF_UNIX && sa->type == SOCK_STREAM && sa->u.sockaddr_un.sun_path[0] != '\0') { orig_sa = sa; sa = nxt_sockaddr_alloc(mp, sa->socklen + 4, sa->length + 4); if (sa == NULL) { goto fail; } sa->type = SOCK_STREAM; sa->u.sockaddr_un.sun_family = AF_UNIX; p = nxt_cpystr((u_char *) sa->u.sockaddr_un.sun_path, (u_char *) orig_sa->u.sockaddr_un.sun_path); nxt_memcpy(p, ".tmp", 4); nxt_sockaddr_text(sa); (void) unlink(sa->u.sockaddr_un.sun_path); } else { orig_sa = NULL; } #endif if (nxt_socket_bind(task, s, sa) != NXT_OK) { goto fail; } #if (NXT_HAVE_UNIX_DOMAIN) if (family == AF_UNIX) { const char *user; const char *group; nxt_runtime_t *rt = thr->runtime; name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; access = rt->control_mode > 0 ? rt->control_mode : 0600; if (nxt_file_set_access(name, access) != NXT_OK) { goto listen_fail; } user = rt->control_user; group = rt->control_group; if (nxt_file_chown(name, user, group) != NXT_OK) { goto listen_fail; } } #endif nxt_debug(task, "listen(%d, %d)", s, ls->backlog); if (listen(s, ls->backlog) != 0) { nxt_alert(task, "listen(%d, %d) failed %E", s, ls->backlog, nxt_socket_errno); goto listen_fail; } #if (NXT_HAVE_UNIX_DOMAIN) if (orig_sa != NULL) { ts = nxt_socket_create(task, AF_UNIX, SOCK_STREAM, 0, 0); if (ts == -1) { goto listen_fail; } ret = connect(ts, &orig_sa->u.sockaddr, orig_sa->socklen); err = nxt_socket_errno; nxt_socket_close(task, ts); if (ret == 0) { nxt_alert(task, "connect(%d, %*s) socket already in use", ts, (size_t) orig_sa->length, nxt_sockaddr_start(orig_sa)); goto listen_fail; } if (err != NXT_ENOENT && err != NXT_ECONNREFUSED) { nxt_alert(task, "connect(%d, %*s) failed %E", ts, (size_t) orig_sa->length, nxt_sockaddr_start(orig_sa), err); goto listen_fail; } tmp = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; name = (nxt_file_name_t *) orig_sa->u.sockaddr_un.sun_path; if (nxt_file_rename(tmp, name) != NXT_OK) { goto listen_fail; } } #endif ls->socket = s; thr->log = old; return NXT_OK; listen_fail: #if (NXT_HAVE_UNIX_DOMAIN) if (family == AF_UNIX) { name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; (void) nxt_file_delete(name); } #endif fail: if (s != -1) { nxt_socket_close(task, s); } thr->log = old; return NXT_ERROR; } nxt_int_t nxt_listen_socket_update(nxt_task_t *task, nxt_listen_socket_t *ls, nxt_listen_socket_t *prev) { nxt_log_t log, *old; nxt_thread_t *thr; ls->socket = prev->socket; thr = nxt_thread(); old = thr->log; log = *thr->log; log.ctx_handler = nxt_listen_socket_log_handler; log.ctx = ls->sockaddr; thr->log = &log; nxt_debug(task, "listen(%d, %d)", ls->socket, ls->backlog); if (listen(ls->socket, ls->backlog) != 0) { nxt_alert(task, "listen(%d, %d) failed %E", ls->socket, ls->backlog, nxt_socket_errno); goto fail; } thr->log = old; return NXT_OK; fail: thr->log = old; return NXT_ERROR; } void nxt_listen_socket_remote_size(nxt_listen_socket_t *ls) { switch (ls->sockaddr->u.sockaddr.sa_family) { #if (NXT_INET6) case AF_INET6: ls->socklen = sizeof(struct sockaddr_in6); ls->address_length = NXT_INET6_ADDR_STR_LEN; break; #endif #if (NXT_HAVE_UNIX_DOMAIN) case AF_UNIX: /* * A remote socket is usually unbound and thus has unspecified Unix * domain sockaddr_un which can be shortcut to 3 bytes. To handle * a bound remote socket correctly ls->socklen should be larger, see * comment in nxt_socket.h. */ ls->socklen = offsetof(struct sockaddr_un, sun_path) + 1; ls->address_length = nxt_length("unix:"); break; #endif default: case AF_INET: ls->socklen = sizeof(struct sockaddr_in); ls->address_length = NXT_INET_ADDR_STR_LEN; break; } } size_t nxt_listen_socket_pool_min_size(nxt_listen_socket_t *ls) { size_t size; /* * The first nxt_sockaddr_t is intended for mandatory remote sockaddr * and textual representaion with port. The second nxt_sockaddr_t * is intended for local sockaddr without textual representaion which * may be required to get specific address of connection received on * wildcard AF_INET and AF_INET6 addresses. For AF_UNIX addresses * the local sockaddr is not required. */ switch (ls->sockaddr->u.sockaddr.sa_family) { #if (NXT_INET6) case AF_INET6: ls->socklen = sizeof(struct sockaddr_in6); ls->address_length = NXT_INET6_ADDR_STR_LEN; size = offsetof(nxt_sockaddr_t, u) + sizeof(struct sockaddr_in6) + NXT_INET6_ADDR_STR_LEN + nxt_length(":65535"); if (IN6_IS_ADDR_UNSPECIFIED(&ls->sockaddr->u.sockaddr_in6.sin6_addr)) { size += offsetof(nxt_sockaddr_t, u) + sizeof(struct sockaddr_in6); } break; #endif #if (NXT_HAVE_UNIX_DOMAIN) case AF_UNIX: /* * A remote socket is usually unbound and thus has unspecified Unix * domain sockaddr_un which can be shortcut to 3 bytes. To handle * a bound remote socket correctly ls->socklen should be at least * sizeof(struct sockaddr_un), see comment in nxt_socket.h. */ ls->socklen = 3; size = ls->socklen + nxt_length("unix:"); ls->address_length = nxt_length("unix:"); break; #endif default: ls->socklen = sizeof(struct sockaddr_in); ls->address_length = NXT_INET_ADDR_STR_LEN; size = offsetof(nxt_sockaddr_t, u) + sizeof(struct sockaddr_in) + NXT_INET_ADDR_STR_LEN + nxt_length(":65535"); if (ls->sockaddr->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) { size += offsetof(nxt_sockaddr_t, u) + sizeof(struct sockaddr_in); } break; } #if (NXT_TLS) if (ls->tls) { size += 4 * sizeof(void *) /* SSL/TLS connection */ + sizeof(nxt_buf_mem_t) + sizeof(nxt_work_t); /* nxt_mp_cleanup */ } #endif return size // + sizeof(nxt_mem_pool_t) + sizeof(nxt_conn_t) + sizeof(nxt_log_t); } static u_char * nxt_listen_socket_log_handler(void *ctx, u_char *pos, u_char *end) { nxt_sockaddr_t *sa; sa = ctx; return nxt_sprintf(pos, end, " while creating listening socket on %*s", (size_t) sa->length, nxt_sockaddr_start(sa)); }