/* * $Id$ * Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "../cmn/mem-prv.h" #include #include #include #include #include #include #include #include // strerror #include #if defined(HAVE_NET_IF_H) # include #endif #if defined(HAVE_SYS_IOCTL_H) # include #endif #if defined(HAVE_IFADDRS_H) # include #endif ///////////////////////////////// QSE_BEGIN_NAMESPACE(QSE) ///////////////////////////////// #include "../cmn/syserr.h" IMPLEMENT_SYSERR_TO_ERRNUM (Socket::ErrorNumber, Socket::) Socket::Socket () QSE_CPP_NOEXCEPT: handle(QSE_INVALID_SCKHND), domain(-1), cached_binding_port(0) { } Socket::~Socket () QSE_CPP_NOEXCEPT { this->close (); } #if 0 int Socket::fdopen (int handle) QSE_CPP_NOEXCEPT { this->handle = handle; } #endif int Socket::open (int domain, int type, int protocol, int traits) QSE_CPP_NOEXCEPT { int x; #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) if (traits & Socket::T_NONBLOCK) type |= SOCK_NONBLOCK; if (traits & Socket::T_CLOEXEC) type |= SOCK_CLOEXEC; open_socket: #endif x = ::socket(domain, type, protocol); if (x == -1) { #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) if (errno == EINVAL && (type & (SOCK_NONBLOCK | SOCK_CLOEXEC))) { type &= ~(SOCK_NONBLOCK | SOCK_CLOEXEC); goto open_socket; } #endif this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } else { #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) if (type & (SOCK_NONBLOCK | SOCK_CLOEXEC)) goto done; #endif } if (traits) { if (((traits & Socket::T_CLOEXEC) && qse_set_sck_cloexec(x, 1) <= -1) || ((traits & Socket::T_NONBLOCK) && qse_set_sck_nonblock(x, 1) <= -1)) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); ::close (x); return -1; } } done: this->close (); // close the existing handle if open. this->handle = x; // while it seems to be possible to get the domain value from a socket // descriptor with getsockopt(SO_DOMAIN), the method doesn't seem universal. // // SO_DOMAIN (since Linux 2.6.32) // Retrieves the socket domain as an integer, returning a value // such as AF_INET6. See socket(2) for details. This socket // option is read-only. // // let me just store the information in the class this->domain = domain; return 0; } void Socket::close () QSE_CPP_NOEXCEPT { if (this->handle != QSE_INVALID_SCKHND) { qse_close_sck (this->handle); this->handle = QSE_INVALID_SCKHND; this->domain = -1; } } int Socket::getSockName (SocketAddress& addr) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_sck_len_t len = addr.getAddrCapa(); int n = ::getsockname(this->handle, (struct sockaddr*)addr.getAddrPtr(), &len); if (n == -1) this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return n; } int Socket::getPeerName (SocketAddress& addr) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_sck_len_t len = addr.getAddrCapa(); int n = ::getpeername(this->handle, (struct sockaddr*)addr.getAddrPtr(), &len); if (n == -1) this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return n; } int Socket::getOption (int level, int optname, void* optval, qse_sck_len_t* optlen) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); int n = ::getsockopt(this->handle, level, optname, (char*)optval, optlen); if (n == -1) this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return n; } int Socket::setOption (int level, int optname, const void* optval, qse_sck_len_t optlen) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); int n = ::setsockopt(this->handle, level, optname, (const char*)optval, optlen); if (n == -1) this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return n; } int Socket::getSendBuf (unsigned int& size) QSE_CPP_NOEXCEPT { qse_sck_len_t optlen = QSE_SIZEOF(size); return this->getOption(SOL_SOCKET, SO_SNDBUF, (char*)&size, &optlen); } int Socket::getRecvBuf (unsigned int& size) QSE_CPP_NOEXCEPT { qse_sck_len_t optlen = QSE_SIZEOF(size); return this->getOption(SOL_SOCKET, SO_RCVBUF, (char*)&size, &optlen); } int Socket::setDebug (int n) QSE_CPP_NOEXCEPT { return this->setOption(SOL_SOCKET, SO_DEBUG, (char*)&n, QSE_SIZEOF(n)); }; int Socket::setReuseAddr (int n) QSE_CPP_NOEXCEPT { return this->setOption(SOL_SOCKET, SO_REUSEADDR, (char*)&n, QSE_SIZEOF(n)); } int Socket::setReusePort (int n) QSE_CPP_NOEXCEPT { #if defined(SO_REUSEPORT) return this->setOption(SOL_SOCKET, SO_REUSEPORT, (char*)&n, QSE_SIZEOF(n)); #else this->setErrorNumber (E_ENOIMPL); return -1; #endif } int Socket::setKeepAlive (int n, int keepidle, int keepintvl, int keepcnt) QSE_CPP_NOEXCEPT { if (this->setOption(SOL_SOCKET, SO_KEEPALIVE, (char*)&n, QSE_SIZEOF(n)) <= -1) return -1; // the following values are just hints. // i don't care about success and failure #if defined(TCP_KEEPIDLE) && defined(SOL_TCP) if (keepidle > 0) this->setOption (SOL_TCP, TCP_KEEPIDLE, (char*)&keepidle, QSE_SIZEOF(keepidle)); #endif #if defined(TCP_KEEPINTVL) && defined(SOL_TCP) if (keepintvl > 0) this->setOption (SOL_TCP, TCP_KEEPINTVL, (char*)&keepintvl, QSE_SIZEOF(keepintvl)); #endif #if defined(TCP_KEEPCNT) && defined(SOL_TCP) if (keepcnt > 0) this->setOption (SOL_TCP, TCP_KEEPCNT, (char*)&keepcnt, QSE_SIZEOF(keepcnt)); #endif return 0; } int Socket::setBroadcast (int n) QSE_CPP_NOEXCEPT { return this->setOption(SOL_SOCKET, SO_BROADCAST, (char*)&n, QSE_SIZEOF(n)); } int Socket::setSendBuf (unsigned int size) QSE_CPP_NOEXCEPT { return this->setOption(SOL_SOCKET, SO_SNDBUF, (char*)&size, QSE_SIZEOF(size)); } int Socket::setRecvBuf (unsigned int size) QSE_CPP_NOEXCEPT { return this->setOption(SOL_SOCKET, SO_RCVBUF, (char*)&size, QSE_SIZEOF(size)); } int Socket::setLingerOn (int sec) QSE_CPP_NOEXCEPT { struct linger lng; lng.l_onoff = 1; lng.l_linger = sec; return this->setOption(SOL_SOCKET, SO_LINGER, (char*)&lng, QSE_SIZEOF(lng)); } int Socket::setLingerOff () QSE_CPP_NOEXCEPT { struct linger lng; lng.l_onoff = 0; lng.l_linger = 0; return this->setOption(SOL_SOCKET, SO_LINGER, (char*)&lng, QSE_SIZEOF(lng)); } int Socket::setTcpNodelay (int n) QSE_CPP_NOEXCEPT { #if defined(TCP_NODELAY) return this->setOption(IPPROTO_TCP, TCP_NODELAY, (char*)&n, QSE_SIZEOF(n)); #else this->setErrorNumber (E_ENOIMPL); return -1; #endif } int Socket::setOobInline (int n) QSE_CPP_NOEXCEPT { #if defined(SO_OOBINLINE) return this->setOption(SOL_SOCKET, SO_OOBINLINE, (char*)&n, QSE_SIZEOF(n)); #else this->setErrorNumber (E_ENOIMPL); return -1; #endif } int Socket::setIpv6Only (int n) QSE_CPP_NOEXCEPT { #if defined(IPV6_V6ONLY) if (this->getDomain() == AF_INET6) return this->setOption(IPPROTO_IPV6, IPV6_V6ONLY, (char*)&n, QSE_SIZEOF(n)); #endif this->setErrorNumber (E_ENOIMPL); return -1; } int Socket::setRecvPktinfo (int n) QSE_CPP_NOEXCEPT { switch (this->getDomain()) { #if defined(IP_PKTINFO) case AF_INET: return this->setOption(IPPROTO_IP, IP_PKTINFO, (char*)&n, QSE_SIZEOF(n)); #endif #if defined(AF_INET6) && defined(IPV6_RECVPKTINFO) case AF_INET6: return this->setOption(IPPROTO_IPV6, IPV6_RECVPKTINFO, (char*)&n, QSE_SIZEOF(n)); break; #endif default: break; } this->setErrorNumber (E_ENOIMPL); return -1; } int Socket::setNonBlock (int n) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); if (qse_set_sck_nonblock(this->handle, n) <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return 0; } int Socket::shutdown (int how) QSE_CPP_NOEXCEPT { if (this->handle != QSE_INVALID_SCKHND) { // i put this guard to allow multiple calls to shutdown(). if (::shutdown(this->handle, how) == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } } return 0; } int Socket::connect (const SocketAddress& target) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); if (::connect(this->handle, (struct sockaddr*)target.getAddrPtr(), target.getAddrSize()) == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return 0; } int Socket::initConnectNB (const SocketAddress& target) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); if (qse_init_sck_conn(this->handle, target.getAddrPtr()) <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return 0; } int Socket::finiConnectNB () QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); int n; if ((n = qse_fini_sck_conn(this->handle)) <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return n; } int Socket::bind (const SocketAddress& target) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); if (::bind(this->handle, (struct sockaddr*)target.getAddrPtr(), target.getAddrSize()) == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } this->cached_binding_port = target.getPort(); return 0; } int Socket::bindToIfceAddr (const qse_mchar_t* ifce, qse_uint16_t port) QSE_CPP_NOEXCEPT { SocketAddress addr; if (this->getIfceAddress(ifce, &addr) <= -1) return -1; return this->bind(addr); } int Socket::bindToIfceAddr (const qse_wchar_t* ifce, qse_uint16_t port) QSE_CPP_NOEXCEPT { SocketAddress addr; if (this->getIfceAddress(ifce, &addr) <= -1) return -1; return this->bind(addr); } int Socket::bindToIfce (const qse_mchar_t* ifce) QSE_CPP_NOEXCEPT { #if defined(SO_BINDTODEVICE) if (!ifce) { return this->setOption (SOL_SOCKET, SO_BINDTODEVICE, QSE_NULL, 0); } else { struct ifreq ifr; QSE_MEMSET (&ifr, 0, QSE_SIZEOF(ifr)); qse_size_t mlen = qse_mbsxcpy (ifr.ifr_name, QSE_COUNTOF(ifr.ifr_name), ifce); if (ifce[mlen] != QSE_MT('\0')) { this->setErrorNumber (E_EINVAL); return -1; } return this->setOption (SOL_SOCKET, SO_BINDTODEVICE, (char*)&ifr, QSE_SIZEOF(ifr)); } #else this->setErrorNumber (E_ENOIMPL); return -1; #endif } int Socket::bindToIfce (const qse_wchar_t* ifce) QSE_CPP_NOEXCEPT { #if defined(SO_BINDTODEVICE) if (!ifce) { return this->setOption(SOL_SOCKET, SO_BINDTODEVICE, QSE_NULL, 0); } else { struct ifreq ifr; QSE_MEMSET (&ifr, 0, QSE_SIZEOF(ifr)); qse_size_t wlen, mlen = QSE_COUNTOF(ifr.ifr_name); if (qse_wcstombs(ifce, &wlen, ifr.ifr_name, &mlen) <= -1 || ifce[wlen] != QSE_WT('\0')) { this->setErrorNumber (E_EINVAL); return -1; } return this->setOption(SOL_SOCKET, SO_BINDTODEVICE, (char*)&ifr, QSE_SIZEOF(ifr)); } #else this->setErrorNumber (E_ENOIMPL); return -1; #endif } int Socket::listen (int backlog) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); if (::listen(this->handle, backlog) == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return 0; } int Socket::accept (Socket* newsck, SocketAddress* newaddr, int traits) QSE_CPP_NOEXCEPT { int newfd, flag_v; qse_sklen_t addrlen; QSE_ASSERT (qse_is_sck_valid(this->handle)); #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) && defined(HAVE_ACCEPT4) flag_v = 0; if (traits & Socket::T_NONBLOCK ) flag_v |= SOCK_NONBLOCK; if (traits & Socket::T_CLOEXEC) flag_v |= SOCK_CLOEXEC; addrlen = newaddr->getAddrCapa(); newfd = ::accept4(this->handle, (struct sockaddr*)newaddr->getAddrPtr(), &addrlen, flag_v); if (newfd == -1) { if (errno != ENOSYS) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } // go on for the normal 3-parameter accept } else { goto accept_done; } #endif addrlen = newaddr->getAddrCapa(); newfd = ::accept(this->handle, (struct sockaddr*)newaddr->getAddrPtr(), &addrlen); if (newfd == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } if (traits) { flag_v = ::fcntl(newfd, F_GETFL, 0); if (flag_v == -1) { fcntl_failure: this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); ::close (newfd); return -1; } else { if (traits & Socket::T_NONBLOCK) flag_v |= O_NONBLOCK; else flag_v &= ~O_NONBLOCK; #if defined(FD_CLOEXEC) if (traits & Socket::T_CLOEXEC) flag_v |= FD_CLOEXEC; else flag_v &= ~FD_CLOEXEC; #endif if (::fcntl(newfd, F_SETFL, flag_v) == -1) goto fcntl_failure; } } accept_done: newsck->handle = newfd; newsck->domain = this->domain; // the new socket inherits the listener's domain. return 0; } qse_ssize_t Socket::send (const void* buf, qse_size_t len) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_ssize_t n = ::send(this->handle, buf, len, 0); if (n == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return n; } qse_ssize_t Socket::send (const void* buf, qse_size_t len, const SocketAddress& dstaddr) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_ssize_t n = ::sendto(this->handle, buf, len, 0, (struct sockaddr*)dstaddr.getAddrPtr(), dstaddr.getAddrSize()); if (n == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return n; } qse_ssize_t Socket::send (const void* buf, qse_size_t len, const SocketAddress& dstaddr, const SocketAddress& srcaddr) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_ioptl_t iov; iov.ptr = (void*)buf; iov.len = len; return this->send(&iov, 1, dstaddr, srcaddr); } qse_ssize_t Socket::send (const qse_ioptl_t* iov, int count) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) ssize_t nwritten; #if defined(HAVE_SENDMSG) struct msghdr msg; QSE_MEMSET (&msg, 0, QSE_SIZEOF(msg)); msg.msg_iov = (struct iovec*)iov; msg.msg_iovlen = count; nwritten = ::sendmsg(this->handle, &msg, 0); #else nwritten = ::writev(this->handle, (const struct iovec*)iov, count); #endif if (nwritten <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return nwritten; #else // TODO: combine to a single buffer .... use sendto.... this->setErrorNumber (E_ENOIMPL); return -1; #endif } qse_ssize_t Socket::send (const qse_ioptl_t* iov, int count, const SocketAddress& dstaddr) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); #if defined(HAVE_SENDMSG) ssize_t nwritten; struct msghdr msg; QSE_MEMSET (&msg, 0, QSE_SIZEOF(msg)); msg.msg_name = (void*)dstaddr.getAddrPtr(); msg.msg_namelen = dstaddr.getAddrSize(); msg.msg_iov = (struct iovec*)iov; msg.msg_iovlen = count; nwritten = ::sendmsg(this->handle, &msg, 0); if (nwritten <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return nwritten; #else // TODO: combine to a single buffer .... use sendto.... this->setErrorNumber (E_ENOIMPL); return -1; #endif } qse_ssize_t Socket::send (const qse_ioptl_t* iov, int count, const SocketAddress& dstaddr, const SocketAddress& srcaddr) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); #if defined(HAVE_SENDMSG) ssize_t nwritten; struct msghdr msg; QSE_MEMSET (&msg, 0, QSE_SIZEOF(msg)); msg.msg_name = (void*)dstaddr.getAddrPtr(); msg.msg_namelen = dstaddr.getAddrSize(); msg.msg_iov = (struct iovec*)iov; msg.msg_iovlen = count; switch (srcaddr.getFamily()) { #if defined(AF_INET) case AF_INET: { #if defined(IP_PKTINFO) qse_uint8_t cmsgbuf[CMSG_SPACE(QSE_SIZEOF(struct in_pktinfo))]; msg.msg_control = cmsgbuf; msg.msg_controllen = CMSG_LEN(QSE_SIZEOF(struct in_pktinfo)); struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_PKTINFO; cmsg->cmsg_len = CMSG_LEN(QSE_SIZEOF(struct in_pktinfo)); struct in_pktinfo* pi = (struct in_pktinfo*)CMSG_DATA(cmsg); //QSE_MEMSET(pi, 0, QSE_SIZEOF(*pi)); pi->ipi_addr = *(struct in_addr*)srcaddr.getIp4addr(); pi->ipi_ifindex = 0; // let the kernel choose it break; #elif defined(IP_SENDSRCADDR) qse_uint8_t cmsgbuf[CMSG_SPACE(QSE_SIZEOF(struct in_addr))]; msg.msg_control = cmsgbuf; msg.msg_controllen = CMSG_LEN(QSE_SIZEOF(struct in_addr)); struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_SENDSRCADDR; cmsg->cmsg_len = CMSG_LEN(QSE_SIZEOF(struct in_addr)); struct in_addr* pi = (struct in_addr*)CMSG_DATA(cmsg); *pi = *(struct in_addr*)srcaddr.getIp6addr(); #else this->setErrorNumber (E_ENOIMPL); return -1; #endif } #endif #if defined(AF_INET6) case AF_INET6: { qse_uint8_t cmsgbuf[CMSG_SPACE(QSE_SIZEOF(struct in6_pktinfo))]; msg.msg_control = cmsgbuf; msg.msg_controllen = CMSG_LEN(QSE_SIZEOF(struct in6_pktinfo));; struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(QSE_SIZEOF(struct in6_pktinfo));; struct in6_pktinfo* pi = (struct in6_pktinfo*)CMSG_DATA(cmsg); //QSE_MEMSET(pi, 0, QSE_SIZEOF(*pi)); pi->ipi6_addr = *(struct in6_addr*)srcaddr.getIp6addr(); pi->ipi6_ifindex = 0; // let the kernel choose it break; } #endif } nwritten = ::sendmsg(this->handle, &msg, 0); if (nwritten <= -1) { this->setErrorNumber (syserr_to_errnum(errno)); return -1; } return nwritten; #else // TODO: combine to a single buffer .... use sendto.... this->setErrorNumber (E_ENOIMPL); return -1; #endif } int Socket::sendx (const void* buf, qse_size_t len, qse_size_t* total_sent) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_size_t pos = 0; while (pos < len) { qse_ssize_t n = ::send(this->handle, (char*)buf + pos, len - pos, 0); if (n <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); if (total_sent) *total_sent = pos; return -1; } pos += n; } if (total_sent) *total_sent = pos; return 0; } int Socket::sendx (const void* buf, qse_size_t len, const SocketAddress& dstaddr, qse_size_t* total_sent) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_size_t pos = 0; while (pos < len) { qse_ssize_t n = ::sendto(this->handle, (char*)buf + pos, len - pos, 0, (struct sockaddr*)dstaddr.getAddrPtr(), dstaddr.getAddrSize()); if (n == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); if (total_sent) *total_sent = pos; return -1; } pos += n; } if (total_sent) *total_sent = pos; return 0; } int Socket::sendx (qse_ioptl_t* iov, int count, qse_size_t* total_sent) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) int index = 0; qse_size_t total = 0; int backup_index = -1; qse_ioptl_t backup; #if defined(HAVE_SENDMSG) struct msghdr msg; QSE_MEMSET (&msg, 0, QSE_SIZEOF(msg)); #endif while (1) { ssize_t nwritten; #if defined(HAVE_SENDMSG) msg.msg_iov = (struct iovec*)&iov[index]; msg.msg_iovlen = count - index; nwritten = ::sendmsg(this->handle, &msg, 0); #else nwritten = ::writev(this->handle, (const struct iovec*)&iov[index], count - index); #endif if (nwritten <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); if (backup_index >= 0) iov[backup_index] = backup; if (total_sent) *total_sent = total; return -1; } total += nwritten; while (index < count && (qse_size_t)nwritten >= iov[index].len) nwritten -= iov[index++].len; if (index == count) break; if (backup_index != index) { if (backup_index >= 0) iov[backup_index] = backup; backup = iov[index]; backup_index = index; } iov[index].ptr = (void*)((qse_uint8_t*)iov[index].ptr + nwritten); iov[index].len -= nwritten; } if (backup_index >= 0) iov[backup_index] = backup; if (total_sent) *total_sent = total; return 0; #else qse_ioptl_t* v, * ve; qse_size_t total = 0, pos, rem; ssize_t nwritten; v = iov; ve = v + count; while (v < ve) { if (v->len <= 0) { v++; continue; } pos = 0; rem = v->len; write_again: nwritten = ::send(this->handle, (qse_uint8_t*)v->ptr + pos, rem, 0); if (nwritten <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); if (total_sent) *total_sent = total; return -1; } total += nwritten; if ((qse_size_t)nwritten < rem) { pos += nwritten; rem -= nwritten; goto write_again; } v++; } if (total_sent) *total_sent = total; return 0; #endif } int Socket::sendx (qse_ioptl_t* iov, int count, const SocketAddress& dstaddr, qse_size_t* total_sent) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); #if defined(HAVE_SENDMSG) int index = 0; qse_size_t total = 0; int backup_index = -1; qse_ioptl_t backup; struct msghdr msg; QSE_MEMSET (&msg, 0, QSE_SIZEOF(msg)); msg.msg_name = (void*)dstaddr.getAddrPtr(); msg.msg_namelen = dstaddr.getAddrSize(); while (1) { ssize_t nwritten; msg.msg_iov = (struct iovec*)&iov[index]; msg.msg_iovlen = count - index; nwritten = ::sendmsg(this->handle, &msg, 0); if (nwritten <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); if (backup_index >= 0) iov[backup_index] = backup; if (total_sent) *total_sent = total; return -1; } total += nwritten; while (index < count && (qse_size_t)nwritten >= iov[index].len) nwritten -= iov[index++].len; if (index == count) break; if (backup_index != index) { if (backup_index >= 0) iov[backup_index] = backup; backup = iov[index]; backup_index = index; } iov[index].ptr = (void*)((qse_uint8_t*)iov[index].ptr + nwritten); iov[index].len -= nwritten; } if (backup_index >= 0) iov[backup_index] = backup; if (total_sent) *total_sent = total; return 0; #else qse_ioptl_t* v, * ve; qse_size_t total = 0, pos, rem; ssize_t nwritten; v = iov; ve = v + count; while (v < ve) { if (v->len <= 0) { v++; continue; } pos = 0; rem = v->len; write_again: nwritten = ::sendto(this->handle, (qse_uint8_t*)v->ptr + pos, rem, 0); if (nwritten <= -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); if (total_sent) *total_sent = total; return -1; } total += nwritten; if ((qse_size_t)nwritten < rem) { pos += nwritten; rem -= nwritten; goto write_again; } v++; } if (total_sent) *total_sent = total; return 0; #endif } qse_ssize_t Socket::receive (void* buf, qse_size_t len) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_ssize_t n = ::recv(this->handle, buf, len, 0); if (n == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return n; } qse_ssize_t Socket::receive (void* buf, qse_size_t len, SocketAddress& srcaddr) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); qse_sklen_t addrlen = srcaddr.getAddrCapa(); qse_ssize_t n = ::recvfrom(this->handle, buf, len, 0, (struct sockaddr*)srcaddr.getAddrPtr(), &addrlen); if (n == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } return n; } qse_ssize_t Socket::receive (void* buf, qse_size_t len, SocketAddress& srcaddr, int& ifindex, SocketAddress* dstaddr) QSE_CPP_NOEXCEPT { QSE_ASSERT (qse_is_sck_valid(this->handle)); #if defined(HAVE_RECVMSG) switch (this->getDomain()) { case AF_INET: { ssize_t n; struct msghdr msg; struct cmsghdr* cmsg; union { struct cmsghdr cmsg; qse_uint8_t buf[CMSG_SPACE(QSE_SIZEOF(struct in_pktinfo))]; } c; struct iovec iov; struct sockaddr_in from; QSE_MEMSET (&msg, 0, QSE_SIZEOF(msg)); msg.msg_control = c.buf; msg.msg_controllen = QSE_SIZEOF(c); msg.msg_flags = 0; msg.msg_name = &from; msg.msg_namelen = QSE_SIZEOF(from); iov.iov_base = buf; iov.iov_len = len; msg.msg_iov = &iov; msg.msg_iovlen = 1; n = ::recvmsg(this->handle, &msg, 0); if (n == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } ifindex = 0; // not known yet #if defined(IP_PKTINFO) for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { struct in_pktinfo* pi; pi = (struct in_pktinfo*)CMSG_DATA(cmsg); ifindex = pi->ipi_ifindex; if (dstaddr) { dstaddr->setFamily (QSE_AF_INET); dstaddr->setIp4addr ((const qse_ip4ad_t*)&pi->ipi_addr); dstaddr->setPort (this->cached_binding_port); } break; } } #endif srcaddr.set ((const qse_skad_t*)&from); return n; } #if defined(AF_INET6) case AF_INET6: { ssize_t n; struct msghdr msg; struct cmsghdr* cmsg; union { struct cmsghdr cmsg; qse_uint8_t buf[CMSG_SPACE(QSE_SIZEOF(struct in6_pktinfo))]; } c; struct iovec iov; struct sockaddr_in6 from; QSE_MEMSET (&msg, 0, QSE_SIZEOF(msg)); msg.msg_control = c.buf; msg.msg_controllen = QSE_SIZEOF(c); msg.msg_flags = 0; msg.msg_name = &from; msg.msg_namelen = QSE_SIZEOF(from); iov.iov_base = buf; iov.iov_len = len; msg.msg_iov = &iov; msg.msg_iovlen = 1; n = ::recvmsg(this->handle, &msg, 0); if (n == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } ifindex = 0; // not known yet #if defined(IPV6_PKTINFO) for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { struct in6_pktinfo* pi; pi = (struct in6_pktinfo*)CMSG_DATA(cmsg); ifindex = pi->ipi6_ifindex; if (dstaddr) { dstaddr->setFamily (QSE_AF_INET6); dstaddr->setIp6addr ((const qse_ip6ad_t*)&pi->ipi6_addr); dstaddr->setPort (this->cached_binding_port); } break; } } #endif srcaddr.set ((const qse_skad_t*)&from); return n; } #endif } this->setErrorFmt (E_ENOIMPL, QSE_T("unsupported socket domain"), strerror(errno)); return -1; #else this->setErrorNumber (E_ENOIMPL); return -1; #endif } int Socket::joinMulticastGroup (const SocketAddress& mcaddr, const SocketAddress& ifaddr) QSE_CPP_NOEXCEPT { int family = mcaddr.getFamily(); // ((struct sockaddr*)mcaddr.getAddrPtr())->sa_family if (family != ifaddr.getFamily()) { this->setErrorNumber (E_EINVAL); return -1; } switch (family) { #if defined(AF_INET) case AF_INET: { struct ip_mreq mreq; QSE_MEMSET (&mreq, 0, QSE_SIZEOF(mreq)); mreq.imr_multiaddr = *(struct in_addr*)mcaddr.getIp4addr(); mreq.imr_interface = *(struct in_addr*)ifaddr.getIp4addr(); return ::setsockopt(this->handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, QSE_SIZEOF(mreq)); } #endif #if defined(AF_INET6) case AF_INET6: { struct ipv6_mreq mreq; QSE_MEMSET (&mreq, 0, QSE_SIZEOF(mreq)); mreq.ipv6mr_multiaddr = *(struct in6_addr*)mcaddr.getIp6addr(); mreq.ipv6mr_interface = ifaddr.getScopeId(); return ::setsockopt(this->handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, QSE_SIZEOF(mreq)); } #endif } this->setErrorNumber (E_ENOIMPL); return -1; } int Socket::leaveMulticastGroup (const SocketAddress& mcaddr, const SocketAddress& ifaddr) QSE_CPP_NOEXCEPT { int family = mcaddr.getFamily(); // ((struct sockaddr*)mcaddr.getAddrPtr())->sa_family if (family != ifaddr.getFamily()) { this->setErrorNumber (E_EINVAL); return -1; } switch (family) { #if defined(AF_INET) case AF_INET: { struct ip_mreq mreq; QSE_MEMSET (&mreq, 0, QSE_SIZEOF(mreq)); mreq.imr_multiaddr = *(struct in_addr*)mcaddr.getIp4addr(); mreq.imr_interface = *(struct in_addr*)ifaddr.getIp4addr(); return ::setsockopt(this->handle, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, QSE_SIZEOF(mreq)); } #endif #if defined(AF_INET6) case AF_INET6: { struct ipv6_mreq mreq; QSE_MEMSET (&mreq, 0, QSE_SIZEOF(mreq)); mreq.ipv6mr_multiaddr = *(struct in6_addr*)mcaddr.getIp6addr(); mreq.ipv6mr_interface = ifaddr.getScopeId(); return ::setsockopt(this->handle, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, QSE_SIZEOF(mreq)); } #endif } this->setErrorNumber (E_ENOIMPL); return -1; } int Socket::getIfceIndex (const qse_mchar_t* name) QSE_CPP_NOEXCEPT { return this->get_ifce_index(name, qse_mbslen(name), false); } int Socket::getIfceIndex (const qse_wchar_t* name) QSE_CPP_NOEXCEPT { return this->get_ifce_index(name, qse_wcslen(name), false); } int Socket::getIfceIndex (const qse_mchar_t* name, qse_size_t len) QSE_CPP_NOEXCEPT { return this->get_ifce_index(name, len, false); } int Socket::getIfceIndex (const qse_wchar_t* name, qse_size_t len) QSE_CPP_NOEXCEPT { return this->get_ifce_index(name, len, false); } int Socket::get_ifce_index (const void* name, qse_size_t len, bool wchar) { #if defined(SIOCGIFINDEX) struct ifreq ifr; QSE_MEMSET (&ifr, 0, QSE_SIZEOF(ifr)); if (wchar) { qse_size_t wlen = len, mlen = QSE_COUNTOF(ifr.ifr_name) - 1; if (qse_wcsntombsn((const qse_wchar_t*)name, &wlen, ifr.ifr_name, &mlen) <= -1 || wlen != len) { this->setErrorNumber (E_EINVAL); return -1; } ifr.ifr_name[mlen] = QSE_MT('\0'); } else { qse_size_t mlen = qse_mbsxncpy(ifr.ifr_name, QSE_COUNTOF(ifr.ifr_name), (const qse_mchar_t*)name, len); if (mlen != len) { this->setErrorNumber (E_EINVAL); return -1; } } if (::ioctl(this->handle, SIOCGIFINDEX, &ifr) == -1) { this->setErrorFmt (syserr_to_errnum(errno), QSE_T("%hs"), strerror(errno)); return -1; } #if defined(ifr_ifindex) return ifr.ifr_ifindex; #else return ifr.ifr_index; #endif #else this->setErrorNumber (E_ENOIMPL); return -1; #endif } // ---------------------------------------------------------------------------- int Socket::getIfceAddress (const qse_mchar_t* name, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFADDR, name, qse_mbslen(name), false, addr, this->domain); } int Socket::getIfceAddress (const qse_wchar_t* name, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFADDR, name, qse_wcslen(name), true, addr, this->domain); } int Socket::getIfceNetmask(const qse_mchar_t* name, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFNETMASK, name, qse_mbslen(name), false, addr, this->domain); } int Socket::getIfceNetmask (const qse_wchar_t* name, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFNETMASK, name, qse_wcslen(name), true, addr, this->domain); } int Socket::getIfceBroadcast(const qse_mchar_t* name, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFBRDADDR, name, qse_mbslen(name), false, addr, this->domain); } int Socket::getIfceBroadcast (const qse_wchar_t* name, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFBRDADDR, name, qse_wcslen(name), true, addr, this->domain); } // ---------------------------------------------------------------------------- int Socket::getIfceAddress (const qse_mchar_t* name, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFADDR, name, qse_mbslen(name), false, addr, family); } int Socket::getIfceAddress (const qse_wchar_t* name, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFADDR, name, qse_wcslen(name), true, addr, family); } int Socket::getIfceNetmask(const qse_mchar_t* name, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFNETMASK, name, qse_mbslen(name), false, addr, family); } int Socket::getIfceNetmask (const qse_wchar_t* name, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFNETMASK, name, qse_wcslen(name), true, addr, family); } int Socket::getIfceBroadcast(const qse_mchar_t* name, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFBRDADDR, name, qse_mbslen(name), false, addr, family); } int Socket::getIfceBroadcast (const qse_wchar_t* name, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFBRDADDR, name, qse_wcslen(name), true, addr, family); } // ---------------------------------------------------------------------------- int Socket::getIfceAddress (const qse_mchar_t* name, qse_size_t len, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFADDR, name, len, false, addr, this->domain); } int Socket::getIfceAddress (const qse_wchar_t* name, qse_size_t len, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFADDR, name, len, true, addr, this->domain); } int Socket::getIfceNetmask(const qse_mchar_t* name, qse_size_t len, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFNETMASK, name, len, false, addr, this->domain); } int Socket::getIfceNetmask (const qse_wchar_t* name, qse_size_t len, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFNETMASK, name, len, true, addr, this->domain); } int Socket::getIfceBroadcast(const qse_mchar_t* name, qse_size_t len, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFBRDADDR, name, len, false, addr, this->domain); } int Socket::getIfceBroadcast (const qse_wchar_t* name, qse_size_t len, SocketAddress* addr) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFBRDADDR, name, len, true, addr, this->domain); } // ---------------------------------------------------------------------------- int Socket::getIfceAddress (const qse_mchar_t* name, qse_size_t len, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFADDR, name, len, false, addr, family); } int Socket::getIfceAddress (const qse_wchar_t* name, qse_size_t len, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFADDR, name, len, true, addr, family); } int Socket::getIfceNetmask(const qse_mchar_t* name, qse_size_t len, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFNETMASK, name, len, false, addr, family); } int Socket::getIfceNetmask (const qse_wchar_t* name, qse_size_t len, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFNETMASK, name, len, true, addr, family); } int Socket::getIfceBroadcast(const qse_mchar_t* name, qse_size_t len, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFBRDADDR, name, len, false, addr, family); } int Socket::getIfceBroadcast (const qse_wchar_t* name, qse_size_t len, SocketAddress* addr, int family) QSE_CPP_NOEXCEPT { return this->get_ifce_address(SIOCGIFBRDADDR, name, len, true, addr, family); } // ---------------------------------------------------------------------------- int Socket::get_ifce_address (int cmd, const void* name, qse_size_t len, bool wchar, SocketAddress* addr, int family) { struct ifreq ifr; QSE_MEMSET (&ifr, 0, QSE_SIZEOF(ifr)); if (wchar) { qse_size_t wlen = len, mlen = QSE_COUNTOF(ifr.ifr_name) - 1; if (qse_wcsntombsn((const qse_wchar_t*)name, &wlen, ifr.ifr_name, &mlen) <= -1 || wlen != len) { this->setErrorNumber (E_EINVAL); return -1; } ifr.ifr_name[mlen] = QSE_MT('\0'); } else { qse_size_t mlen = qse_mbsxncpy(ifr.ifr_name, QSE_COUNTOF(ifr.ifr_name), (const qse_mchar_t*)name, len); if (mlen != len) { this->setErrorNumber (E_EINVAL); return -1; } } #if defined(HAVE_GETIFADDRS) struct ifaddrs* ifa; if (::getifaddrs(&ifa) == 0) { for (struct ifaddrs* ife = ifa; ife; ife = ife->ifa_next) { if (qse_mbscmp(ifr.ifr_name, ife->ifa_name) != 0) continue; struct sockaddr* sa = QSE_NULL; switch (cmd) { case SIOCGIFADDR: sa = ife->ifa_addr; break; case SIOCGIFNETMASK: sa = ife->ifa_netmask; break; case SIOCGIFBRDADDR: sa = ife->ifa_broadaddr; break; default: break; } if (!sa || !sa->sa_data) continue; if (sa->sa_family != family) continue; /* skip an address that doesn't match given domain */ //*addr = SocketAddress((const qse_skad_t*)sa); addr->set ((const qse_skad_t*)sa); freeifaddrs (ifa); return 0; } freeifaddrs (ifa); } #endif if (::ioctl(this->handle, cmd, &ifr) == -1) { this->setErrorNumber (syserr_to_errnum(errno)); return -1; } struct sockaddr* sa = (struct sockaddr*)&ifr.ifr_addr; if (sa->sa_family != this->domain) { this->setErrorNumber (E_ENOENT); return -1; } //*addr = SocketAddress((const qse_skad_t*)&ifr.ifr_addr); addr->set ((const qse_skad_t*)&ifr.ifr_addr); return 0; } ///////////////////////////////// QSE_END_NAMESPACE(QSE) /////////////////////////////////