/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2021 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "shrpx_quic.h" #include #include #include #include #include #include #include #include #include "shrpx_config.h" #include "shrpx_log.h" #include "util.h" #include "xsi_strerror.h" using namespace nghttp2; namespace shrpx { ngtcp2_tstamp quic_timestamp() { return std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count(); } int create_quic_server_socket(UpstreamAddr &faddr) { std::array errbuf; int fd = -1; int rv; auto service = util::utos(faddr.port); addrinfo hints{}; hints.ai_family = faddr.family; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; #ifdef AI_ADDRCONFIG hints.ai_flags |= AI_ADDRCONFIG; #endif // AI_ADDRCONFIG auto node = faddr.host == StringRef::from_lit("*") ? nullptr : faddr.host.c_str(); addrinfo *res, *rp; rv = getaddrinfo(node, service.c_str(), &hints, &res); #ifdef AI_ADDRCONFIG if (rv != 0) { // Retry without AI_ADDRCONFIG hints.ai_flags &= ~AI_ADDRCONFIG; rv = getaddrinfo(node, service.c_str(), &hints, &res); } #endif // AI_ADDRCONFIG if (rv != 0) { LOG(FATAL) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6") << " address for " << faddr.host << ", port " << faddr.port << ": " << gai_strerror(rv); return -1; } auto res_d = defer(freeaddrinfo, res); std::array host; for (rp = res; rp; rp = rp->ai_next) { rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host.data(), host.size(), nullptr, 0, NI_NUMERICHOST); if (rv != 0) { LOG(WARN) << "getnameinfo() failed: " << gai_strerror(rv); continue; } #ifdef SOCK_NONBLOCK fd = socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC, rp->ai_protocol); if (fd == -1) { auto error = errno; LOG(WARN) << "socket() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); continue; } #else // !SOCK_NONBLOCK fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (fd == -1) { auto error = errno; LOG(WARN) << "socket() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); continue; } util::make_socket_nonblocking(fd); util::make_socket_closeonexec(fd); #endif // !SOCK_NONBLOCK int val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set SO_REUSEPORT option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } if (faddr.family == AF_INET6) { #ifdef IPV6_V6ONLY if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IPV6_V6ONLY option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } #endif // IPV6_V6ONLY if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IPV6_RECVPKTINFO option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } } else { if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &val, static_cast(sizeof(val))) == -1) { auto error = errno; LOG(WARN) << "Failed to set IP_PKTINFO option to listener socket: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } } // TODO Enable ECN if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) { auto error = errno; LOG(WARN) << "bind() syscall failed: " << xsi_strerror(error, errbuf.data(), errbuf.size()); close(fd); continue; } break; } if (!rp) { LOG(FATAL) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6") << " socket failed"; return -1; } faddr.fd = fd; faddr.hostport = util::make_http_hostport(mod_config()->balloc, StringRef{host.data()}, faddr.port); LOG(NOTICE) << "Listening on " << faddr.hostport << ", quic"; return 0; } int quic_send_packet(const UpstreamAddr *addr, const sockaddr *remote_sa, size_t remote_salen, const sockaddr *local_sa, size_t local_salen, const uint8_t *data, size_t datalen, size_t gso_size) { return 0; } int generate_quic_connection_id(ngtcp2_cid *cid, size_t cidlen) { if (RAND_bytes(cid->data, cidlen) != 1) { return -1; } cid->datalen = cidlen; return 0; } int generate_quic_stateless_reset_token(uint8_t *token, const ngtcp2_cid *cid, const uint8_t *secret, size_t secretlen) { ngtcp2_crypto_md md; ngtcp2_crypto_md_init(&md, const_cast(EVP_sha256())); if (ngtcp2_crypto_generate_stateless_reset_token(token, &md, secret, secretlen, cid) != 0) { return -1; } return 0; } } // namespace shrpx