| /** @file | |
| A brief file description | |
| @section license License | |
| 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. | |
| */ | |
| /*************************************************************************** | |
| Socket operations | |
| ***************************************************************************/ | |
| #include "swoc/bwf_ip.h" | |
| #include "tscore/ink_sock.h" | |
| #include "tscore/ink_platform.h" | |
| #include "tscore/ink_assert.h" | |
| #include "tscore/ink_string.h" | |
| #include "tscore/ink_inet.h" | |
| // | |
| // Compilation options | |
| // | |
| // #define CHECK_PLAUSIBILITY_OF_SOCKADDR | |
| #ifdef CHECK_PLAUSIBILITY_OF_SOCKADDR | |
| #define CHECK_PLAUSIBLE_SOCKADDR(_n, _f, _l) check_plausible_sockaddr(_n, _f, _n) | |
| inline void | |
| check_valid_sockaddr(sockaddr *sa, char *file, int line) | |
| { | |
| sockaddr_in *si = (sockaddr_in *)sa; | |
| unsigned short port = ntohs(si->sin_port); | |
| unsigned short addr = ntohl(si->sin_addr.s_addr); | |
| if (port > 20000) { | |
| cerr << "[byteordering] In " << file << ", line " << line << " the IP port "; | |
| cerr << "was found to be " << port << "(in host byte order).\n"; | |
| cerr << "[byteordering] This seems implausible, so check for byte order problems\n"; | |
| } | |
| } | |
| #else | |
| #define CHECK_PLAUSIBLE_SOCKADDR(_n, _f, _l) | |
| #endif | |
| int | |
| safe_setsockopt(int s, int level, int optname, const void *optval, int optlen) | |
| { | |
| int r; | |
| do { | |
| r = setsockopt(s, level, optname, optval, optlen); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| return r; | |
| } | |
| int | |
| safe_getsockopt(int s, int level, int optname, void *optval, int *optlen) | |
| { | |
| int r; | |
| do { | |
| r = getsockopt(s, level, optname, optval, reinterpret_cast<socklen_t *>(optlen)); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| return r; | |
| } | |
| int | |
| safe_fcntl(int fd, int cmd, int arg) | |
| { | |
| int r = 0; | |
| do { | |
| r = fcntl(fd, cmd, arg); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| return r; | |
| } | |
| int | |
| safe_set_fl(int fd, int arg) | |
| { | |
| int flags = safe_fcntl(fd, F_GETFL, 0); | |
| if (flags < 0) { | |
| return flags; | |
| } | |
| flags |= arg; | |
| flags = safe_fcntl(fd, F_SETFL, flags); | |
| return flags; | |
| } | |
| int | |
| safe_clr_fl(int fd, int arg) | |
| { | |
| int flags = safe_fcntl(fd, F_GETFL, 0); | |
| if (flags < 0) { | |
| return flags; | |
| } | |
| flags &= ~arg; | |
| flags = safe_fcntl(fd, F_SETFL, flags); | |
| return flags; | |
| } | |
| std::int64_t | |
| safe_write(int fd, const char *buffer, int len) | |
| { | |
| std::int64_t num_written = 0; | |
| while (num_written < len) { | |
| int const ret = write(fd, buffer + num_written, len - num_written); | |
| if (ret == -1) { | |
| if (errno == EAGAIN || errno == EINTR) { | |
| // The errno indicates that we should attempt the write again. Poll | |
| // until the socket is ready for writing then try again. | |
| if (write_ready(fd) != 1) { | |
| return -1; | |
| } | |
| continue; | |
| } | |
| // Some unexpected errno was encountered. | |
| return ret; | |
| } | |
| num_written += ret; | |
| } | |
| return num_written; | |
| } | |
| int | |
| safe_nonblocking(int fd) | |
| { | |
| return safe_set_fl(fd, O_NONBLOCK); | |
| } | |
| int | |
| safe_blocking(int fd) | |
| { | |
| return safe_clr_fl(fd, O_NONBLOCK); | |
| } | |
| int | |
| read_ready(int fd, int timeout_msec) | |
| { | |
| struct pollfd p; | |
| p.events = POLLIN; | |
| p.fd = fd; | |
| int r = poll(&p, 1, timeout_msec); | |
| if (r <= 0) { | |
| return r; | |
| } | |
| if (p.revents & (POLLERR | POLLNVAL)) { | |
| return -1; | |
| } | |
| if (p.revents & (POLLIN | POLLHUP)) { | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| int | |
| write_ready(int fd, int timeout_msec) | |
| { | |
| struct pollfd p; | |
| p.events = POLLOUT; | |
| p.fd = fd; | |
| int r = poll(&p, 1, timeout_msec); | |
| if (r <= 0) { | |
| return r; | |
| } | |
| if (p.revents & (POLLERR | POLLNVAL)) { | |
| return -1; | |
| } | |
| if (p.revents & POLLOUT) { | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| int | |
| safe_ioctl(int fd, int request, char *arg) | |
| { | |
| int r = -1; | |
| do { | |
| r = ioctl(fd, request, arg); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| return r; | |
| } | |
| int | |
| safe_bind(int s, struct sockaddr const *name, int namelen) | |
| { | |
| int r; | |
| CHECK_PLAUSIBLE_SOCKADDR(name, __FILE__, __LINE__); | |
| do { | |
| r = bind(s, name, namelen); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| return r; | |
| } | |
| int | |
| safe_listen(int s, int backlog) | |
| { | |
| int r; | |
| do { | |
| r = listen(s, backlog); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| return r; | |
| } | |
| int | |
| safe_getsockname(int s, struct sockaddr *name, int *namelen) | |
| { | |
| int r; | |
| do { | |
| r = getsockname(s, name, reinterpret_cast<socklen_t *>(namelen)); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| return r; | |
| } | |
| int | |
| safe_getpeername(int s, struct sockaddr *name, int *namelen) | |
| { | |
| int r; | |
| do { | |
| r = getpeername(s, name, reinterpret_cast<socklen_t *>(namelen)); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| return r; | |
| } | |
| char | |
| fd_read_char(int fd) | |
| { | |
| char c; | |
| int r; | |
| do { | |
| r = read(fd, &c, 1); | |
| if (r > 0) { | |
| return c; | |
| } | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| perror("fd_read_char"); | |
| ink_assert(!"fd_read_char"); | |
| return c; | |
| } | |
| int | |
| fd_read_line(int fd, char *s, int len) | |
| { | |
| char c; | |
| int numread = 0, r; | |
| do { | |
| do { | |
| r = read(fd, &c, 1); | |
| } while (r < 0 && (errno == EAGAIN || errno == EINTR)); | |
| if (r <= 0 && numread) { | |
| break; | |
| } | |
| if (r <= 0) { | |
| return r; | |
| } | |
| if (c == '\n') { | |
| break; | |
| } | |
| s[numread++] = c; | |
| } while (numread < len - 1); | |
| s[numread] = 0; | |
| return numread; | |
| } | |
| int | |
| close_socket(int s) | |
| { | |
| return close(s); | |
| } | |
| int | |
| write_socket(int s, const char *buffer, int length) | |
| { | |
| return write(s, (const void *)buffer, length); | |
| } | |
| int | |
| read_socket(int s, char *buffer, int length) | |
| { | |
| return read(s, (void *)buffer, length); | |
| } | |
| int | |
| bind_unix_domain_socket(const char *path, mode_t mode) | |
| { | |
| int sockfd; | |
| struct sockaddr_un sockaddr; | |
| socklen_t socklen; | |
| (void)unlink(path); | |
| sockfd = socket(AF_UNIX, SOCK_STREAM, 0); | |
| if (sockfd < 0) { | |
| return sockfd; | |
| } | |
| if (strlen(path) > sizeof(sockaddr.sun_path) - 1) { | |
| errno = ENAMETOOLONG; | |
| goto fail; | |
| } | |
| ink_zero(sockaddr); | |
| sockaddr.sun_family = AF_UNIX; | |
| ink_strlcpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path)); | |
| #if defined(darwin) || defined(freebsd) | |
| socklen = sizeof(struct sockaddr_un); | |
| #else | |
| socklen = strlen(sockaddr.sun_path) + sizeof(sockaddr.sun_family); | |
| #endif | |
| if (setsockopt_on(sockfd, SOL_SOCKET, SO_REUSEADDR) < 0) { | |
| goto fail; | |
| } | |
| if (safe_fcntl(sockfd, F_SETFD, FD_CLOEXEC) < 0) { | |
| goto fail; | |
| } | |
| if (bind(sockfd, reinterpret_cast<struct sockaddr *>(&sockaddr), socklen) < 0) { | |
| goto fail; | |
| } | |
| if (chmod(path, mode) < 0) { | |
| goto fail; | |
| } | |
| if (listen(sockfd, 5) < 0) { | |
| goto fail; | |
| } | |
| return sockfd; | |
| fail: | |
| int errsav = errno; | |
| close(sockfd); | |
| errno = errsav; | |
| return -1; | |
| } |