| /** @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. |
| */ |
| |
| /************************************************************************** |
| Connections |
| |
| Commonality across all platforms -- move out as required. |
| |
| **************************************************************************/ |
| #include "P_DNS.h" |
| #include "P_DNSConnection.h" |
| #include "P_DNSProcessor.h" |
| |
| #define SET_TCP_NO_DELAY |
| #define SET_NO_LINGER |
| #define SET_SO_KEEPALIVE |
| // set in the OS |
| // #define RECV_BUF_SIZE (1024*64) |
| // #define SEND_BUF_SIZE (1024*64) |
| #define FIRST_RANDOM_PORT (16000) |
| #define LAST_RANDOM_PORT (60000) |
| |
| #define ROUNDUP(x, y) ((((x) + ((y)-1)) / (y)) * (y)) |
| |
| DNSConnection::Options const DNSConnection::DEFAULT_OPTIONS; |
| |
| // |
| // Functions |
| // |
| |
| DNSConnection::DNSConnection() |
| : fd(NO_FD), generator(static_cast<uint32_t>(static_cast<uintptr_t>(time(nullptr)) ^ (uintptr_t)this)) |
| { |
| memset(&ip, 0, sizeof(ip)); |
| } |
| |
| DNSConnection::~DNSConnection() |
| { |
| close(); |
| } |
| |
| int |
| DNSConnection::close() |
| { |
| eio.stop(); |
| // don't close any of the standards |
| if (fd >= 2) { |
| int fd_save = fd; |
| fd = NO_FD; |
| return socketManager.close(fd_save); |
| } else { |
| fd = NO_FD; |
| return -EBADF; |
| } |
| } |
| |
| void |
| DNSConnection::trigger() |
| { |
| handler->triggered.enqueue(this); |
| |
| // Since the periodic check is removed, we need to call |
| // this when it's triggered by EVENTIO_DNS_CONNECTION. |
| // The handler should be pointing to DNSHandler::mainEvent. |
| // We can schedule an immediate event or call the handler |
| // directly, and since both arguments are not being used |
| // passing in 0 and nullptr will do the job. |
| handler->handleEvent(0, nullptr); |
| } |
| |
| int |
| DNSConnection::connect(sockaddr const *addr, Options const &opt) |
| // bool non_blocking_connect, bool use_tcp, bool non_blocking, bool bind_random_port) |
| { |
| ink_assert(fd == NO_FD); |
| ink_assert(ats_is_ip(addr)); |
| this->opt = opt; |
| this->tcp_data.reset(); |
| |
| int res = 0; |
| short Proto; |
| uint8_t af = addr->sa_family; |
| IpEndpoint bind_addr; |
| size_t bind_size = 0; |
| |
| if (opt._use_tcp) { |
| Proto = IPPROTO_TCP; |
| if ((res = socketManager.socket(af, SOCK_STREAM, 0)) < 0) { |
| goto Lerror; |
| } |
| } else { |
| Proto = IPPROTO_UDP; |
| if ((res = socketManager.socket(af, SOCK_DGRAM, 0)) < 0) { |
| goto Lerror; |
| } |
| } |
| |
| fd = res; |
| |
| memset(&bind_addr, 0, sizeof bind_addr); |
| bind_addr.sa.sa_family = af; |
| |
| if (AF_INET6 == af) { |
| if (ats_is_ip6(opt._local_ipv6)) { |
| ats_ip_copy(&bind_addr.sa, opt._local_ipv6); |
| } else { |
| bind_addr.sin6.sin6_addr = in6addr_any; |
| } |
| bind_size = sizeof(sockaddr_in6); |
| } else if (AF_INET == af) { |
| if (ats_is_ip4(opt._local_ipv4)) { |
| ats_ip_copy(&bind_addr.sa, opt._local_ipv4); |
| } else { |
| bind_addr.sin.sin_addr.s_addr = INADDR_ANY; |
| } |
| bind_size = sizeof(sockaddr_in); |
| } else { |
| ink_assert(!"Target DNS address must be IP."); |
| } |
| |
| if (opt._bind_random_port) { |
| int retries = 0; |
| IpEndpoint bind_addr; |
| size_t bind_size = 0; |
| memset(&bind_addr, 0, sizeof bind_addr); |
| bind_addr.sa.sa_family = af; |
| if (AF_INET6 == af) { |
| bind_addr.sin6.sin6_addr = in6addr_any; |
| bind_size = sizeof bind_addr.sin6; |
| } else { |
| bind_addr.sin.sin_addr.s_addr = INADDR_ANY; |
| bind_size = sizeof bind_addr.sin; |
| } |
| while (retries++ < 10000) { |
| ip_port_text_buffer b; |
| uint32_t p = generator.random(); |
| p = static_cast<uint16_t>((p % (LAST_RANDOM_PORT - FIRST_RANDOM_PORT)) + FIRST_RANDOM_PORT); |
| ats_ip_port_cast(&bind_addr.sa) = htons(p); // stuff port in sockaddr. |
| Debug("dns", "random port = %s", ats_ip_nptop(&bind_addr.sa, b, sizeof b)); |
| if (socketManager.ink_bind(fd, &bind_addr.sa, bind_size, Proto) < 0) { |
| continue; |
| } |
| goto Lok; |
| } |
| Warning("unable to bind random DNS port"); |
| Lok:; |
| } else if (ats_is_ip(&bind_addr.sa)) { |
| ip_text_buffer b; |
| res = socketManager.ink_bind(fd, &bind_addr.sa, bind_size, Proto); |
| if (res < 0) { |
| Warning("Unable to bind local address to %s.", ats_ip_ntop(&bind_addr.sa, b, sizeof b)); |
| } |
| } |
| |
| if (opt._non_blocking_connect) { |
| if ((res = safe_nonblocking(fd)) < 0) { |
| goto Lerror; |
| } |
| } |
| |
| // cannot do this after connection on non-blocking connect |
| #ifdef SET_TCP_NO_DELAY |
| if (opt._use_tcp) { |
| if ((res = safe_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, SOCKOPT_ON, sizeof(int))) < 0) { |
| goto Lerror; |
| } |
| } |
| #endif |
| #ifdef RECV_BUF_SIZE |
| socketManager.set_rcvbuf_size(fd, RECV_BUF_SIZE); |
| #endif |
| #ifdef SET_SO_KEEPALIVE |
| // enables 2 hour inactivity probes, also may fix IRIX FIN_WAIT_2 leak |
| if ((res = safe_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, SOCKOPT_ON, sizeof(int))) < 0) { |
| goto Lerror; |
| } |
| #endif |
| |
| ats_ip_copy(&ip.sa, addr); |
| res = ::connect(fd, addr, ats_ip_size(addr)); |
| |
| if (!res || ((res < 0) && (errno == EINPROGRESS || errno == EWOULDBLOCK))) { |
| if (!opt._non_blocking_connect && opt._non_blocking_io) { |
| if ((res = safe_nonblocking(fd)) < 0) { |
| goto Lerror; |
| } |
| } |
| // Shouldn't we turn off non-blocking when it's a non-blocking connect |
| // and blocking IO? |
| } else { |
| goto Lerror; |
| } |
| |
| return 0; |
| |
| Lerror: |
| if (fd != NO_FD) { |
| close(); |
| } |
| return res; |
| } |