diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /lib/util/util_net.c | |
parent | Initial commit. (diff) | |
download | samba-upstream.tar.xz samba-upstream.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/util/util_net.c')
-rw-r--r-- | lib/util/util_net.c | 1145 |
1 files changed, 1145 insertions, 0 deletions
diff --git a/lib/util/util_net.c b/lib/util/util_net.c new file mode 100644 index 0000000..a0032cc --- /dev/null +++ b/lib/util/util_net.c @@ -0,0 +1,1145 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1992-2007 + Copyright (C) Simo Sorce 2001 + Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003. + Copyright (C) James J Myers 2003 + Copyright (C) Tim Potter 2000-2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/network.h" +#include "system/locale.h" +#include "system/filesys.h" +#include "lib/util/util_net.h" + +#undef strcasecmp +#undef strncasecmp + +/******************************************************************* + Set an address to INADDR_ANY. +******************************************************************/ + +void zero_sockaddr(struct sockaddr_storage *pss) +{ + /* Ensure we're at least a valid sockaddr-storage. */ + *pss = (struct sockaddr_storage) { .ss_family = AF_INET }; +} + +static char *normalize_ipv6_literal(const char *str, char *buf, size_t *_len) +{ +#define IPv6_LITERAL_NET ".ipv6-literal.net" + const size_t llen = sizeof(IPv6_LITERAL_NET) - 1; + size_t len = *_len; + int cmp; + size_t i; + size_t idx_chars = 0; + size_t cnt_delimiter = 0; + size_t cnt_chars = 0; + + if (len <= llen) { + return NULL; + } + + /* ignore a trailing '.' */ + if (str[len - 1] == '.') { + len -= 1; + } + + len -= llen; + if (len >= INET6_ADDRSTRLEN) { + return NULL; + } + if (len < 2) { + return NULL; + } + + cmp = strncasecmp(&str[len], IPv6_LITERAL_NET, llen); + if (cmp != 0) { + return NULL; + } + + for (i = 0; i < len; i++) { + if (idx_chars != 0) { + break; + } + + switch (str[i]) { + case '-': + buf[i] = ':'; + cnt_chars = 0; + cnt_delimiter += 1; + break; + case 's': + buf[i] = SCOPE_DELIMITER; + idx_chars += 1; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + buf[i] = str[i]; + cnt_chars += 1; + break; + default: + return NULL; + } + if (cnt_chars > 4) { + return NULL; + } + if (cnt_delimiter > 7) { + return NULL; + } + } + + if (cnt_delimiter < 2) { + return NULL; + } + + for (; idx_chars != 0 && i < len; i++) { + switch (str[i]) { + case SCOPE_DELIMITER: + case ':': + return NULL; + default: + buf[i] = str[i]; + idx_chars += 1; + break; + } + } + + if (idx_chars == 1) { + return NULL; + } + + buf[i] = '\0'; + *_len = len; + return buf; +} + +/** + * Wrap getaddrinfo... + */ +bool interpret_string_addr_internal(struct addrinfo **ppres, + const char *str, int flags) +{ + int ret; + struct addrinfo hints; +#if defined(HAVE_IPV6) + char addr[INET6_ADDRSTRLEN*2] = { 0, }; + unsigned int scope_id = 0; + size_t len = strlen(str); +#endif + + ZERO_STRUCT(hints); + + /* By default make sure it supports TCP. */ + hints.ai_socktype = SOCK_STREAM; + + /* always try as a numeric host first. This prevents unnecessary name + * lookups, and also ensures we accept IPv6 addresses */ + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + +#if defined(HAVE_IPV6) + if (len < sizeof(addr)) { + char *p = NULL; + + p = normalize_ipv6_literal(str, addr, &len); + if (p != NULL) { + hints.ai_family = AF_INET6; + str = p; + } + } + + if (strchr_m(str, ':')) { + char *p = strchr_m(str, SCOPE_DELIMITER); + + /* + * Cope with link-local. + * This is IP:v6:addr%ifname. + */ + + if (p && (p > str) && ((scope_id = if_nametoindex(p+1)) != 0)) { + /* Length of string we want to copy. + This is IP:v6:addr (removing the %ifname). + */ + len = PTR_DIFF(p,str); + + if (len+1 > sizeof(addr)) { + /* string+nul too long for array. */ + return false; + } + if (str != addr) { + memcpy(addr, str, len); + } + addr[len] = '\0'; + + str = addr; + } + } +#endif + + ret = getaddrinfo(str, NULL, &hints, ppres); + if (ret == 0) { +#if defined(HAVE_IPV6) + struct sockaddr_in6 *ps6 = NULL; + + if (scope_id == 0) { + return true; + } + if (ppres == NULL) { + return true; + } + if ((*ppres) == NULL) { + return true; + } + if ((*ppres)->ai_addr->sa_family != AF_INET6) { + return true; + } + + ps6 = (struct sockaddr_in6 *)(*ppres)->ai_addr; + + if (IN6_IS_ADDR_LINKLOCAL(&ps6->sin6_addr) && + ps6->sin6_scope_id == 0) { + ps6->sin6_scope_id = scope_id; + } +#endif + + return true; + } + + hints.ai_flags = flags; + + /* Linux man page on getaddrinfo() says port will be + uninitialized when service string is NULL */ + + ret = getaddrinfo(str, NULL, + &hints, + ppres); + + if (ret) { + DEBUG(3, ("interpret_string_addr_internal: " + "getaddrinfo failed for name %s (flags %d) [%s]\n", + str, flags, gai_strerror(ret))); + return false; + } + return true; +} + +/******************************************************************* + Map a text hostname or IP address (IPv4 or IPv6) into a + struct sockaddr_storage. Takes a flag which allows it to + prefer an IPv4 address (needed for DC's). +******************************************************************/ + +static bool interpret_string_addr_pref(struct sockaddr_storage *pss, + const char *str, + int flags, + bool prefer_ipv4) +{ + struct addrinfo *res = NULL; + int int_flags; + + zero_sockaddr(pss); + + if (flags & AI_NUMERICHOST) { + int_flags = flags; + } else { + int_flags = flags|AI_ADDRCONFIG; + } + + if (!interpret_string_addr_internal(&res, str, int_flags)) { + return false; + } + if (!res) { + return false; + } + + if (prefer_ipv4) { + struct addrinfo *p; + + for (p = res; p; p = p->ai_next) { + if (p->ai_family == AF_INET) { + memcpy(pss, p->ai_addr, p->ai_addrlen); + break; + } + } + if (p == NULL) { + /* Copy the first sockaddr. */ + memcpy(pss, res->ai_addr, res->ai_addrlen); + } + } else { + /* Copy the first sockaddr. */ + memcpy(pss, res->ai_addr, res->ai_addrlen); + } + + freeaddrinfo(res); + return true; +} + +/******************************************************************* + Map a text hostname or IP address (IPv4 or IPv6) into a + struct sockaddr_storage. Address agnostic version. +******************************************************************/ + +bool interpret_string_addr(struct sockaddr_storage *pss, + const char *str, + int flags) +{ + return interpret_string_addr_pref(pss, + str, + flags, + false); +} + +/******************************************************************* + Map a text hostname or IP address (IPv4 or IPv6) into a + struct sockaddr_storage. Version that prefers IPv4. +******************************************************************/ + +bool interpret_string_addr_prefer_ipv4(struct sockaddr_storage *pss, + const char *str, + int flags) +{ + return interpret_string_addr_pref(pss, + str, + flags, + true); +} + +/** + * Interpret an internet address or name into an IP address in 4 byte form. + * RETURNS IN NETWORK BYTE ORDER (big endian). + */ + +uint32_t interpret_addr(const char *str) +{ + uint32_t ret; + + /* If it's in the form of an IP address then + * get the lib to interpret it */ + if (is_ipaddress_v4(str)) { + struct in_addr dest; + + if (inet_pton(AF_INET, str, &dest) <= 0) { + /* Error - this shouldn't happen ! */ + DEBUG(0,("interpret_addr: inet_pton failed " + "host %s\n", + str)); + return 0; + } + ret = dest.s_addr; /* NETWORK BYTE ORDER ! */ + } else { + /* Otherwise assume it's a network name of some sort and use + getaddrinfo. */ + struct addrinfo *res = NULL; + struct addrinfo *res_list = NULL; + if (!interpret_string_addr_internal(&res_list, + str, + AI_ADDRCONFIG)) { + DEBUG(3,("interpret_addr: Unknown host. %s\n",str)); + return 0; + } + + /* Find the first IPv4 address. */ + for (res = res_list; res; res = res->ai_next) { + if (res->ai_family != AF_INET) { + continue; + } + if (res->ai_addr == NULL) { + continue; + } + break; + } + if(res == NULL) { + DEBUG(3,("interpret_addr: host address is " + "invalid for host %s\n",str)); + if (res_list) { + freeaddrinfo(res_list); + } + return 0; + } + memcpy((char *)&ret, + &((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr, + sizeof(ret)); + if (res_list) { + freeaddrinfo(res_list); + } + } + + /* This is so bogus - all callers need fixing... JRA. */ + if (ret == (uint32_t)-1) { + return 0; + } + + return ret; +} + +/** + A convenient addition to interpret_addr(). +**/ +_PUBLIC_ struct in_addr interpret_addr2(const char *str) +{ + struct in_addr ret; + uint32_t a = interpret_addr(str); + ret.s_addr = a; + return ret; +} + +/** + Check if an IP is the 0.0.0.0. +**/ + +_PUBLIC_ bool is_zero_ip_v4(struct in_addr ip) +{ + return ip.s_addr == 0; +} + +/** + Are two IPs on the same subnet? +**/ + +_PUBLIC_ bool same_net_v4(struct in_addr ip1, struct in_addr ip2, struct in_addr mask) +{ + uint32_t net1,net2,nmask; + + nmask = ntohl(mask.s_addr); + net1 = ntohl(ip1.s_addr); + net2 = ntohl(ip2.s_addr); + + return((net1 & nmask) == (net2 & nmask)); +} + +/** + * Return true if a string could be an IPv4 address. + */ + +bool is_ipaddress_v4(const char *str) +{ + int ret = -1; + struct in_addr dest; + + ret = inet_pton(AF_INET, str, &dest); + if (ret > 0) { + return true; + } + return false; +} + +bool is_ipv6_literal(const char *str) +{ +#if defined(HAVE_IPV6) + char buf[INET6_ADDRSTRLEN*2] = { 0, }; + size_t len = strlen(str); + char *p = NULL; + + if (len >= sizeof(buf)) { + return false; + } + + p = normalize_ipv6_literal(str, buf, &len); + if (p == NULL) { + return false; + } + + return true; +#else + return false; +#endif +} + +/** + * Return true if a string could be a IPv6 address. + */ + +bool is_ipaddress_v6(const char *str) +{ +#if defined(HAVE_IPV6) + int ret = -1; + char *p = NULL; + char buf[INET6_ADDRSTRLEN] = { 0, }; + size_t len; + const char *addr = str; + const char *idxs = NULL; + unsigned int idx = 0; + struct in6_addr ip6; + + p = strchr_m(str, ':'); + if (p == NULL) { + return is_ipv6_literal(str); + } + + p = strchr_m(str, SCOPE_DELIMITER); + if (p && (p > str)) { + len = PTR_DIFF(p, str); + idxs = p + 1; + } else { + len = strlen(str); + } + + if (len >= sizeof(buf)) { + return false; + } + if (idxs != NULL) { + strncpy(buf, str, len); + addr = buf; + } + + /* + * Cope with link-local. + * This is IP:v6:addr%ifidx. + */ + if (idxs != NULL) { + char c; + + ret = sscanf(idxs, "%5u%c", &idx, &c); + if (ret != 1) { + idx = 0; + } + + if (idx > 0 && idx < UINT16_MAX) { + /* a valid index */ + idxs = NULL; + } + } + + /* + * Cope with link-local. + * This is IP:v6:addr%ifname. + */ + if (idxs != NULL) { + idx = if_nametoindex(idxs); + + if (idx > 0) { + /* a valid index */ + idxs = NULL; + } + } + + if (idxs != NULL) { + return false; + } + + ret = inet_pton(AF_INET6, addr, &ip6); + if (ret <= 0) { + return false; + } + + return true; +#else + return false; +#endif +} + +/** + * Return true if a string could be an IPv4 or IPv6 address. + */ + +bool is_ipaddress(const char *str) +{ + return is_ipaddress_v4(str) || is_ipaddress_v6(str); +} + +/** + * Is a sockaddr a broadcast address ? + */ + +bool is_broadcast_addr(const struct sockaddr *pss) +{ +#if defined(HAVE_IPV6) + if (pss->sa_family == AF_INET6) { + const struct in6_addr *sin6 = + &((const struct sockaddr_in6 *)pss)->sin6_addr; + return IN6_IS_ADDR_MULTICAST(sin6); + } +#endif + if (pss->sa_family == AF_INET) { + uint32_t addr = + ntohl(((const struct sockaddr_in *)pss)->sin_addr.s_addr); + return addr == INADDR_BROADCAST; + } + return false; +} + +/** + * Check if an IPv7 is 127.0.0.1 + */ +bool is_loopback_ip_v4(struct in_addr ip) +{ + struct in_addr a; + a.s_addr = htonl(INADDR_LOOPBACK); + return(ip.s_addr == a.s_addr); +} + +/** + * Check if a struct sockaddr is the loopback address. + */ +bool is_loopback_addr(const struct sockaddr *pss) +{ +#if defined(HAVE_IPV6) + if (pss->sa_family == AF_INET6) { + const struct in6_addr *pin6 = + &((const struct sockaddr_in6 *)pss)->sin6_addr; + return IN6_IS_ADDR_LOOPBACK(pin6); + } +#endif + if (pss->sa_family == AF_INET) { + const struct in_addr *pin = &((const struct sockaddr_in *)pss)->sin_addr; + return is_loopback_ip_v4(*pin); + } + return false; +} + +/** + * Check if a struct sockaddr has an unspecified address. + */ +bool is_zero_addr(const struct sockaddr_storage *pss) +{ +#if defined(HAVE_IPV6) + if (pss->ss_family == AF_INET6) { + const struct in6_addr *pin6 = + &((const struct sockaddr_in6 *)pss)->sin6_addr; + return IN6_IS_ADDR_UNSPECIFIED(pin6); + } +#endif + if (pss->ss_family == AF_INET) { + const struct in_addr *pin = &((const struct sockaddr_in *)pss)->sin_addr; + return is_zero_ip_v4(*pin); + } + return false; +} + +/** + * Set an IP to 0.0.0.0. + */ +void zero_ip_v4(struct in_addr *ip) +{ + ZERO_STRUCTP(ip); +} + +bool is_linklocal_addr(const struct sockaddr_storage *pss) +{ +#ifdef HAVE_IPV6 + if (pss->ss_family == AF_INET6) { + const struct in6_addr *pin6 = + &((const struct sockaddr_in6 *)pss)->sin6_addr; + return IN6_IS_ADDR_LINKLOCAL(pin6); + } +#endif + if (pss->ss_family == AF_INET) { + const struct in_addr *pin = + &((const struct sockaddr_in *)pss)->sin_addr; + struct in_addr ll_addr; + struct in_addr mask_addr; + + /* 169.254.0.0/16, is link local, see RFC 3927 */ + ll_addr.s_addr = 0xa9fe0000; + mask_addr.s_addr = 0xffff0000; + return same_net_v4(*pin, ll_addr, mask_addr); + } + return false; +} + +/** + * Convert an IPv4 struct in_addr to a struct sockaddr_storage. + */ +void in_addr_to_sockaddr_storage(struct sockaddr_storage *ss, + struct in_addr ip) +{ + struct sockaddr_in *sa = (struct sockaddr_in *)ss; + ZERO_STRUCTP(ss); + sa->sin_family = AF_INET; + sa->sin_addr = ip; +} + +#if defined(HAVE_IPV6) +/** + * Convert an IPv6 struct in_addr to a struct sockaddr_storage. + */ +void in6_addr_to_sockaddr_storage(struct sockaddr_storage *ss, + struct in6_addr ip) +{ + struct sockaddr_in6 *sa = (struct sockaddr_in6 *)ss; + memset(ss, '\0', sizeof(*ss)); + sa->sin6_family = AF_INET6; + sa->sin6_addr = ip; +} +#endif + +/** + * Are two IPs on the same subnet? + */ +bool same_net(const struct sockaddr *ip1, + const struct sockaddr *ip2, + const struct sockaddr *mask) +{ + if (ip1->sa_family != ip2->sa_family) { + /* Never on the same net. */ + return false; + } + +#if defined(HAVE_IPV6) + if (ip1->sa_family == AF_INET6) { + struct sockaddr_in6 ip1_6 = *(const struct sockaddr_in6 *)ip1; + struct sockaddr_in6 ip2_6 = *(const struct sockaddr_in6 *)ip2; + struct sockaddr_in6 mask_6 = *(const struct sockaddr_in6 *)mask; + char *p1 = (char *)&ip1_6.sin6_addr; + char *p2 = (char *)&ip2_6.sin6_addr; + char *m = (char *)&mask_6.sin6_addr; + size_t i; + + for (i = 0; i < sizeof(struct in6_addr); i++) { + *p1++ &= *m; + *p2++ &= *m; + m++; + } + return (memcmp(&ip1_6.sin6_addr, + &ip2_6.sin6_addr, + sizeof(struct in6_addr)) == 0); + } +#endif + if (ip1->sa_family == AF_INET) { + return same_net_v4(((const struct sockaddr_in *)ip1)->sin_addr, + ((const struct sockaddr_in *)ip2)->sin_addr, + ((const struct sockaddr_in *)mask)->sin_addr); + } + return false; +} + +/** + * Are two sockaddr 's the same family and address ? Ignore port etc. + */ + +bool sockaddr_equal(const struct sockaddr *ip1, + const struct sockaddr *ip2) +{ + if (ip1->sa_family != ip2->sa_family) { + /* Never the same. */ + return false; + } + +#if defined(HAVE_IPV6) + if (ip1->sa_family == AF_INET6) { + return (memcmp(&((const struct sockaddr_in6 *)ip1)->sin6_addr, + &((const struct sockaddr_in6 *)ip2)->sin6_addr, + sizeof(struct in6_addr)) == 0); + } +#endif + if (ip1->sa_family == AF_INET) { + return (memcmp(&((const struct sockaddr_in *)ip1)->sin_addr, + &((const struct sockaddr_in *)ip2)->sin_addr, + sizeof(struct in_addr)) == 0); + } + return false; +} + +/** + * Is an IP address the INADDR_ANY or in6addr_any value ? + */ +bool is_address_any(const struct sockaddr *psa) +{ +#if defined(HAVE_IPV6) + if (psa->sa_family == AF_INET6) { + const struct sockaddr_in6 *si6 = (const struct sockaddr_in6 *)psa; + if (memcmp(&in6addr_any, + &si6->sin6_addr, + sizeof(in6addr_any)) == 0) { + return true; + } + return false; + } +#endif + if (psa->sa_family == AF_INET) { + const struct sockaddr_in *si = (const struct sockaddr_in *)psa; + if (si->sin_addr.s_addr == INADDR_ANY) { + return true; + } + return false; + } + return false; +} + +void set_sockaddr_port(struct sockaddr *psa, uint16_t port) +{ +#if defined(HAVE_IPV6) + if (psa->sa_family == AF_INET6) { + ((struct sockaddr_in6 *)psa)->sin6_port = htons(port); + } +#endif + if (psa->sa_family == AF_INET) { + ((struct sockaddr_in *)psa)->sin_port = htons(port); + } +} + + +/**************************************************************************** + Get a port number in host byte order from a sockaddr_storage. +****************************************************************************/ + +uint16_t get_sockaddr_port(const struct sockaddr_storage *pss) +{ + uint16_t port = 0; + + if (pss->ss_family != AF_INET) { +#if defined(HAVE_IPV6) + /* IPv6 */ + const struct sockaddr_in6 *sa6 = + (const struct sockaddr_in6 *)pss; + port = ntohs(sa6->sin6_port); +#endif + } else { + const struct sockaddr_in *sa = + (const struct sockaddr_in *)pss; + port = ntohs(sa->sin_port); + } + return port; +} + +/**************************************************************************** + Print out an IPv4 or IPv6 address from a struct sockaddr_storage. +****************************************************************************/ + +char *print_sockaddr_len(char *dest, + size_t destlen, + const struct sockaddr *psa, + socklen_t psalen) +{ + if (destlen > 0) { + dest[0] = '\0'; + } + (void)sys_getnameinfo(psa, + psalen, + dest, destlen, + NULL, 0, + NI_NUMERICHOST); + return dest; +} + +/**************************************************************************** + Print out an IPv4 or IPv6 address from a struct sockaddr_storage. +****************************************************************************/ + +char *print_sockaddr(char *dest, + size_t destlen, + const struct sockaddr_storage *psa) +{ + return print_sockaddr_len(dest, destlen, (const struct sockaddr *)psa, + sizeof(struct sockaddr_storage)); +} + +/**************************************************************************** + Print out a canonical IPv4 or IPv6 address from a struct sockaddr_storage. +****************************************************************************/ + +char *print_canonical_sockaddr(TALLOC_CTX *ctx, + const struct sockaddr_storage *pss) +{ + char addr[INET6_ADDRSTRLEN]; + char *dest = NULL; + int ret; + + /* Linux getnameinfo() man pages says port is uninitialized if + service name is NULL. */ + + ret = sys_getnameinfo((const struct sockaddr *)pss, + sizeof(struct sockaddr_storage), + addr, sizeof(addr), + NULL, 0, + NI_NUMERICHOST); + if (ret != 0) { + return NULL; + } + + if (pss->ss_family != AF_INET) { +#if defined(HAVE_IPV6) + dest = talloc_asprintf(ctx, "[%s]", addr); +#else + return NULL; +#endif + } else { + dest = talloc_asprintf(ctx, "%s", addr); + } + + return dest; +} + +enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON}; + +typedef struct smb_socket_option { + const char *name; + int level; + int option; + int value; + int opttype; +} smb_socket_option; + +static const smb_socket_option socket_options[] = { + {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL}, + {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL}, + {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL}, +#ifdef TCP_NODELAY + {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL}, +#endif +#ifdef TCP_KEEPCNT + {"TCP_KEEPCNT", IPPROTO_TCP, TCP_KEEPCNT, 0, OPT_INT}, +#endif +#ifdef TCP_KEEPIDLE + {"TCP_KEEPIDLE", IPPROTO_TCP, TCP_KEEPIDLE, 0, OPT_INT}, +#endif +#ifdef TCP_KEEPINTVL + {"TCP_KEEPINTVL", IPPROTO_TCP, TCP_KEEPINTVL, 0, OPT_INT}, +#endif +#ifdef IPTOS_LOWDELAY + {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON}, +#endif +#ifdef IPTOS_THROUGHPUT + {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON}, +#endif +#ifdef SO_REUSEPORT + {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL}, +#endif +#ifdef SO_SNDBUF + {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT}, +#endif +#ifdef SO_RCVBUF + {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT}, +#endif +#ifdef SO_SNDLOWAT + {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT}, +#endif +#ifdef SO_RCVLOWAT + {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT}, +#endif +#ifdef SO_SNDTIMEO + {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT}, +#endif +#ifdef SO_RCVTIMEO + {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT}, +#endif +#ifdef TCP_FASTACK + {"TCP_FASTACK", IPPROTO_TCP, TCP_FASTACK, 0, OPT_INT}, +#endif +#ifdef TCP_QUICKACK + {"TCP_QUICKACK", IPPROTO_TCP, TCP_QUICKACK, 0, OPT_BOOL}, +#endif +#ifdef TCP_NODELAYACK + {"TCP_NODELAYACK", IPPROTO_TCP, TCP_NODELAYACK, 0, OPT_BOOL}, +#endif +#ifdef TCP_KEEPALIVE_THRESHOLD + {"TCP_KEEPALIVE_THRESHOLD", IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, 0, OPT_INT}, +#endif +#ifdef TCP_KEEPALIVE_ABORT_THRESHOLD + {"TCP_KEEPALIVE_ABORT_THRESHOLD", IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, 0, OPT_INT}, +#endif +#ifdef TCP_DEFER_ACCEPT + {"TCP_DEFER_ACCEPT", IPPROTO_TCP, TCP_DEFER_ACCEPT, 0, OPT_INT}, +#endif +#ifdef TCP_USER_TIMEOUT + {"TCP_USER_TIMEOUT", IPPROTO_TCP, TCP_USER_TIMEOUT, 0, OPT_INT}, +#endif + {NULL,0,0,0,0}}; + +/**************************************************************************** + Print socket options. +****************************************************************************/ + +static void print_socket_options(int s) +{ + TALLOC_CTX *frame = NULL; + const smb_socket_option *p = &socket_options[0]; + char *str = NULL; + + if (DEBUGLEVEL < 5) { + return; + } + + frame = talloc_stackframe(); + + str = talloc_strdup(frame, ""); + if (str == NULL) { + DBG_WARNING("talloc failed\n"); + goto done; + } + + for (; p->name != NULL; p++) { + int ret, val; + socklen_t vlen = sizeof(val); + + ret = getsockopt(s, p->level, p->option, (void *)&val, &vlen); + if (ret == -1) { + DBG_INFO("Could not test socket option %s: %s.\n", + p->name, strerror(errno)); + continue; + } + + str = talloc_asprintf_append_buffer( + str, + "%s%s=%d", + str[0] != '\0' ? ", " : "", + p->name, + val); + if (str == NULL) { + DBG_WARNING("talloc_asprintf_append_buffer failed\n"); + goto done; + } + } + + DEBUG(5, ("socket options: %s\n", str)); +done: + TALLOC_FREE(frame); + } + +/**************************************************************************** + Set user socket options. +****************************************************************************/ + +void set_socket_options(int fd, const char *options) +{ + TALLOC_CTX *ctx = talloc_new(NULL); + char *tok; + + while (next_token_talloc(ctx, &options, &tok," \t,")) { + int ret=0,i; + int value = 1; + char *p; + bool got_value = false; + + if ((p = strchr_m(tok,'='))) { + *p = 0; + value = atoi(p+1); + got_value = true; + } + + for (i=0;socket_options[i].name;i++) + if (strequal(socket_options[i].name,tok)) + break; + + if (!socket_options[i].name) { + DEBUG(0,("Unknown socket option %s\n",tok)); + continue; + } + + switch (socket_options[i].opttype) { + case OPT_BOOL: + case OPT_INT: + ret = setsockopt(fd,socket_options[i].level, + socket_options[i].option, + (char *)&value,sizeof(int)); + break; + + case OPT_ON: + if (got_value) + DEBUG(0,("syntax error - %s " + "does not take a value\n",tok)); + + { + int on = socket_options[i].value; + ret = setsockopt(fd,socket_options[i].level, + socket_options[i].option, + (char *)&on,sizeof(int)); + } + break; + } + + if (ret != 0) { + /* be aware that some systems like Solaris return + * EINVAL to a setsockopt() call when the client + * sent a RST previously - no need to worry */ + DEBUG(2,("Failed to set socket option %s (Error %s)\n", + tok, strerror(errno) )); + } + } + + TALLOC_FREE(ctx); + print_socket_options(fd); +} + +/* + * Utility function that copes only with AF_INET and AF_INET6 + * as that's all we're going to get out of DNS / NetBIOS / WINS + * name resolution functions. + */ + +bool sockaddr_storage_to_samba_sockaddr( + struct samba_sockaddr *sa, const struct sockaddr_storage *ss) +{ + sa->u.ss = *ss; + + switch (ss->ss_family) { + case AF_INET: + sa->sa_socklen = sizeof(struct sockaddr_in); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + sa->sa_socklen = sizeof(struct sockaddr_in6); + break; +#endif + default: + return false; + } + return true; +} + +bool samba_sockaddr_set_port(struct samba_sockaddr *sa, uint16_t port) +{ + if (sa->u.sa.sa_family == AF_INET) { + sa->u.in.sin_port = htons(port); + return true; + } +#ifdef HAVE_IPV6 + if (sa->u.sa.sa_family == AF_INET6) { + sa->u.in6.sin6_port = htons(port); + return true; + } +#endif + return false; +} + +bool samba_sockaddr_get_port(const struct samba_sockaddr *sa, uint16_t *port) +{ + if (sa->u.sa.sa_family == AF_INET) { + *port = ntohs(sa->u.in.sin_port); + return true; + } +#ifdef HAVE_IPV6 + if (sa->u.sa.sa_family == AF_INET6) { + *port = ntohs(sa->u.in6.sin6_port); + return true; + } +#endif + return false; +} |