xref: /aosp_15_r20/external/grpc-grpc/src/core/lib/iomgr/socket_utils_common_posix.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/iomgr/port.h"
22 
23 #ifdef GRPC_POSIX_SOCKET_UTILS_COMMON
24 
25 #include <arpa/inet.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <netinet/in.h>
30 
31 #include "src/core/lib/iomgr/socket_utils.h"
32 #include "src/core/lib/iomgr/socket_utils_posix.h"
33 #ifdef GRPC_LINUX_TCP_H
34 #include <linux/tcp.h>
35 #else
36 #include <netinet/tcp.h>
37 #endif
38 #include <stdio.h>
39 #include <string.h>
40 #include <sys/socket.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 
44 #include <string>
45 
46 #include <grpc/event_engine/endpoint_config.h>
47 #include <grpc/support/alloc.h>
48 #include <grpc/support/log.h>
49 #include <grpc/support/sync.h>
50 
51 #include "src/core/lib/address_utils/sockaddr_utils.h"
52 #include "src/core/lib/gpr/string.h"
53 #include "src/core/lib/gprpp/crash.h"
54 #include "src/core/lib/gprpp/strerror.h"
55 #include "src/core/lib/iomgr/sockaddr.h"
56 
57 // set a socket to use zerocopy
grpc_set_socket_zerocopy(int fd)58 grpc_error_handle grpc_set_socket_zerocopy(int fd) {
59 #ifdef GRPC_LINUX_ERRQUEUE
60   const int enable = 1;
61   auto err = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &enable, sizeof(enable));
62   if (err != 0) {
63     return GRPC_OS_ERROR(errno, "setsockopt(SO_ZEROCOPY)");
64   }
65   return absl::OkStatus();
66 #else
67   (void)fd;
68   return GRPC_OS_ERROR(ENOSYS, "setsockopt(SO_ZEROCOPY)");
69 #endif
70 }
71 
72 // set a socket to non blocking mode
grpc_set_socket_nonblocking(int fd,int non_blocking)73 grpc_error_handle grpc_set_socket_nonblocking(int fd, int non_blocking) {
74   int oldflags = fcntl(fd, F_GETFL, 0);
75   if (oldflags < 0) {
76     return GRPC_OS_ERROR(errno, "fcntl");
77   }
78 
79   if (non_blocking) {
80     oldflags |= O_NONBLOCK;
81   } else {
82     oldflags &= ~O_NONBLOCK;
83   }
84 
85   if (fcntl(fd, F_SETFL, oldflags) != 0) {
86     return GRPC_OS_ERROR(errno, "fcntl");
87   }
88 
89   return absl::OkStatus();
90 }
91 
grpc_set_socket_no_sigpipe_if_possible(int fd)92 grpc_error_handle grpc_set_socket_no_sigpipe_if_possible(int fd) {
93 #ifdef GRPC_HAVE_SO_NOSIGPIPE
94   int val = 1;
95   int newval;
96   socklen_t intlen = sizeof(newval);
97   if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) {
98     return GRPC_OS_ERROR(errno, "setsockopt(SO_NOSIGPIPE)");
99   }
100   if (0 != getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen)) {
101     return GRPC_OS_ERROR(errno, "getsockopt(SO_NOSIGPIPE)");
102   }
103   if ((newval != 0) != (val != 0)) {
104     return GRPC_ERROR_CREATE("Failed to set SO_NOSIGPIPE");
105   }
106 #else
107   // Avoid unused parameter warning for conditional parameter
108   (void)fd;
109 #endif
110   return absl::OkStatus();
111 }
112 
grpc_set_socket_ip_pktinfo_if_possible(int fd)113 grpc_error_handle grpc_set_socket_ip_pktinfo_if_possible(int fd) {
114   // Use conditionally-important parameter to avoid warning
115   (void)fd;
116 #ifdef GRPC_HAVE_IP_PKTINFO
117   int get_local_ip = 1;
118   if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
119                       sizeof(get_local_ip))) {
120     return GRPC_OS_ERROR(errno, "setsockopt(IP_PKTINFO)");
121   }
122 #endif
123   return absl::OkStatus();
124 }
125 
grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd)126 grpc_error_handle grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) {
127   // Use conditionally-important parameter to avoid warning
128   (void)fd;
129 #ifdef GRPC_HAVE_IPV6_RECVPKTINFO
130   int get_local_ip = 1;
131   if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
132                       sizeof(get_local_ip))) {
133     return GRPC_OS_ERROR(errno, "setsockopt(IPV6_RECVPKTINFO)");
134   }
135 #endif
136   return absl::OkStatus();
137 }
138 
grpc_set_socket_sndbuf(int fd,int buffer_size_bytes)139 grpc_error_handle grpc_set_socket_sndbuf(int fd, int buffer_size_bytes) {
140   return 0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size_bytes,
141                          sizeof(buffer_size_bytes))
142              ? absl::OkStatus()
143              : GRPC_OS_ERROR(errno, "setsockopt(SO_SNDBUF)");
144 }
145 
grpc_set_socket_rcvbuf(int fd,int buffer_size_bytes)146 grpc_error_handle grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes) {
147   return 0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size_bytes,
148                          sizeof(buffer_size_bytes))
149              ? absl::OkStatus()
150              : GRPC_OS_ERROR(errno, "setsockopt(SO_RCVBUF)");
151 }
152 
153 // set a socket to close on exec
grpc_set_socket_cloexec(int fd,int close_on_exec)154 grpc_error_handle grpc_set_socket_cloexec(int fd, int close_on_exec) {
155   int oldflags = fcntl(fd, F_GETFD, 0);
156   if (oldflags < 0) {
157     return GRPC_OS_ERROR(errno, "fcntl");
158   }
159 
160   if (close_on_exec) {
161     oldflags |= FD_CLOEXEC;
162   } else {
163     oldflags &= ~FD_CLOEXEC;
164   }
165 
166   if (fcntl(fd, F_SETFD, oldflags) != 0) {
167     return GRPC_OS_ERROR(errno, "fcntl");
168   }
169 
170   return absl::OkStatus();
171 }
172 
173 // set a socket to reuse old addresses
grpc_set_socket_reuse_addr(int fd,int reuse)174 grpc_error_handle grpc_set_socket_reuse_addr(int fd, int reuse) {
175   int val = (reuse != 0);
176   int newval;
177   socklen_t intlen = sizeof(newval);
178   if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) {
179     return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEADDR)");
180   }
181   if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen)) {
182     return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEADDR)");
183   }
184   if ((newval != 0) != val) {
185     return GRPC_ERROR_CREATE("Failed to set SO_REUSEADDR");
186   }
187 
188   return absl::OkStatus();
189 }
190 
191 // set a socket to reuse old addresses
grpc_set_socket_reuse_port(int fd,int reuse)192 grpc_error_handle grpc_set_socket_reuse_port(int fd, int reuse) {
193 #ifndef SO_REUSEPORT
194   return GRPC_ERROR_CREATE("SO_REUSEPORT unavailable on compiling system");
195 #else
196   int val = (reuse != 0);
197   int newval;
198   socklen_t intlen = sizeof(newval);
199   if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))) {
200     return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEPORT)");
201   }
202   if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &newval, &intlen)) {
203     return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEPORT)");
204   }
205   if ((newval != 0) != val) {
206     return GRPC_ERROR_CREATE("Failed to set SO_REUSEPORT");
207   }
208 
209   return absl::OkStatus();
210 #endif
211 }
212 
213 static gpr_once g_probe_so_reuesport_once = GPR_ONCE_INIT;
214 static int g_support_so_reuseport = false;
215 
probe_so_reuseport_once(void)216 void probe_so_reuseport_once(void) {
217   int s = socket(AF_INET, SOCK_STREAM, 0);
218   if (s < 0) {
219     // This might be an ipv6-only environment in which case 'socket(AF_INET,..)'
220     // call would fail. Try creating IPv6 socket in that case
221     s = socket(AF_INET6, SOCK_STREAM, 0);
222   }
223   if (s >= 0) {
224     g_support_so_reuseport = GRPC_LOG_IF_ERROR(
225         "check for SO_REUSEPORT", grpc_set_socket_reuse_port(s, 1));
226     close(s);
227   }
228 }
229 
grpc_is_socket_reuse_port_supported()230 bool grpc_is_socket_reuse_port_supported() {
231   gpr_once_init(&g_probe_so_reuesport_once, probe_so_reuseport_once);
232   return g_support_so_reuseport;
233 }
234 
235 // disable nagle
grpc_set_socket_low_latency(int fd,int low_latency)236 grpc_error_handle grpc_set_socket_low_latency(int fd, int low_latency) {
237   int val = (low_latency != 0);
238   int newval;
239   socklen_t intlen = sizeof(newval);
240   if (0 != setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
241     return GRPC_OS_ERROR(errno, "setsockopt(TCP_NODELAY)");
242   }
243   if (0 != getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen)) {
244     return GRPC_OS_ERROR(errno, "getsockopt(TCP_NODELAY)");
245   }
246   if ((newval != 0) != val) {
247     return GRPC_ERROR_CREATE("Failed to set TCP_NODELAY");
248   }
249   return absl::OkStatus();
250 }
251 
252 /* Set Differentiated Services Code Point (DSCP) */
grpc_set_socket_dscp(int fd,int dscp)253 grpc_error_handle grpc_set_socket_dscp(int fd, int dscp) {
254   if (dscp == grpc_core::PosixTcpOptions::kDscpNotSet) {
255     return absl::OkStatus();
256   }
257   // The TOS/TrafficClass byte consists of following bits:
258   // | 7 6 5 4 3 2 | 1 0 |
259   // |    DSCP     | ECN |
260   int value = dscp << 2;
261 
262   int optval;
263   socklen_t optlen = sizeof(optval);
264   // Get ECN bits from current IP_TOS value unless IPv6 only
265   if (0 == getsockopt(fd, IPPROTO_IP, IP_TOS, &optval, &optlen)) {
266     value |= (optval & 0x3);
267     if (0 != setsockopt(fd, IPPROTO_IP, IP_TOS, &value, sizeof(value))) {
268       return GRPC_OS_ERROR(errno, "setsockopt(IP_TOS)");
269     }
270   }
271   // Get ECN from current Traffic Class value if IPv6 is available
272   if (0 == getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &optval, &optlen)) {
273     value |= (optval & 0x3);
274     if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &value, sizeof(value))) {
275       return GRPC_OS_ERROR(errno, "setsockopt(IPV6_TCLASS)");
276     }
277   }
278   return absl::OkStatus();
279 }
280 
281 // The default values for TCP_USER_TIMEOUT are currently configured to be in
282 // line with the default values of KEEPALIVE_TIMEOUT as proposed in
283 // https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md
284 #define DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS 20000  // 20 seconds
285 #define DEFAULT_SERVER_TCP_USER_TIMEOUT_MS 20000  // 20 seconds
286 
287 static int g_default_client_tcp_user_timeout_ms =
288     DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS;
289 static int g_default_server_tcp_user_timeout_ms =
290     DEFAULT_SERVER_TCP_USER_TIMEOUT_MS;
291 static bool g_default_client_tcp_user_timeout_enabled = false;
292 static bool g_default_server_tcp_user_timeout_enabled = true;
293 
294 #if GPR_LINUX == 1
295 // For Linux, it will be detected to support TCP_USER_TIMEOUT
296 #ifndef TCP_USER_TIMEOUT
297 #define TCP_USER_TIMEOUT 18
298 #endif
299 #define SOCKET_SUPPORTS_TCP_USER_TIMEOUT_DEFAULT 0
300 #else
301 // For non-Linux, TCP_USER_TIMEOUT will be used if TCP_USER_TIMEOUT is defined.
302 #ifdef TCP_USER_TIMEOUT
303 #define SOCKET_SUPPORTS_TCP_USER_TIMEOUT_DEFAULT 0
304 #else
305 #define TCP_USER_TIMEOUT 0
306 #define SOCKET_SUPPORTS_TCP_USER_TIMEOUT_DEFAULT -1
307 #endif  // TCP_USER_TIMEOUT
308 #endif  // GPR_LINUX == 1
309 
310 // Whether the socket supports TCP_USER_TIMEOUT option.
311 // (0: don't know, 1: support, -1: not support)
312 static std::atomic<int> g_socket_supports_tcp_user_timeout(
313     SOCKET_SUPPORTS_TCP_USER_TIMEOUT_DEFAULT);
314 
config_default_tcp_user_timeout(bool enable,int timeout,bool is_client)315 void config_default_tcp_user_timeout(bool enable, int timeout, bool is_client) {
316   if (is_client) {
317     g_default_client_tcp_user_timeout_enabled = enable;
318     if (timeout > 0) {
319       g_default_client_tcp_user_timeout_ms = timeout;
320     }
321   } else {
322     g_default_server_tcp_user_timeout_enabled = enable;
323     if (timeout > 0) {
324       g_default_server_tcp_user_timeout_ms = timeout;
325     }
326   }
327 }
328 
329 // Set TCP_USER_TIMEOUT
330 // As documented in
331 // https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md, the
332 // default values for TCP_USER_TIMEOUT are currently configured to be in line
333 // with the default values of KEEPALIVE_TIMEOUT as proposed in
334 // https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md. In
335 // other words, by default, TCP_USER_TIMEOUT is disabled on clients (since
336 // keepalive is disabled on clients by default), and enabled on servers. To
337 // override the default settings of enabling/disabling TCP_USER_TIMEOUT, the
338 // value of KEEPALIVE_TIME on channel args is used. If present, a value of
339 // INT_MAX means that TCP_USER_TIMEOUT would be disabled, while any other value
340 // would enable TCP_USER_TIMEOUT. If TCP_USER_TIMEOUT is enabled, the default
341 // setting is 20 seconds (aligning with the default of KEEPALIVE_TIMEOUT). The
342 // KEEPALIVE_TIMEOUT channel arg overrides the value used for TCP_USER_TIMEOUT.
grpc_set_socket_tcp_user_timeout(int fd,const grpc_core::PosixTcpOptions & options,bool is_client)343 grpc_error_handle grpc_set_socket_tcp_user_timeout(
344     int fd, const grpc_core::PosixTcpOptions& options, bool is_client) {
345   // Use conditionally-important parameter to avoid warning
346   (void)fd;
347   (void)is_client;
348   extern grpc_core::TraceFlag grpc_tcp_trace;
349   if (g_socket_supports_tcp_user_timeout.load() >= 0) {
350     bool enable;
351     int timeout;
352     if (is_client) {
353       enable = g_default_client_tcp_user_timeout_enabled;
354       timeout = g_default_client_tcp_user_timeout_ms;
355     } else {
356       enable = g_default_server_tcp_user_timeout_enabled;
357       timeout = g_default_server_tcp_user_timeout_ms;
358     }
359     int value = options.keep_alive_time_ms;
360     if (value > 0) {
361       enable = value != INT_MAX;
362     }
363     value = options.keep_alive_timeout_ms;
364     if (value > 0) {
365       timeout = value;
366     }
367     if (enable) {
368       int newval;
369       socklen_t len = sizeof(newval);
370       // If this is the first time to use TCP_USER_TIMEOUT, try to check
371       // if it is available.
372       if (g_socket_supports_tcp_user_timeout.load() == 0) {
373         if (0 != getsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &newval, &len)) {
374           gpr_log(GPR_INFO,
375                   "TCP_USER_TIMEOUT is not available. TCP_USER_TIMEOUT won't "
376                   "be used thereafter");
377           g_socket_supports_tcp_user_timeout.store(-1);
378         } else {
379           gpr_log(GPR_INFO,
380                   "TCP_USER_TIMEOUT is available. TCP_USER_TIMEOUT will be "
381                   "used thereafter");
382           g_socket_supports_tcp_user_timeout.store(1);
383         }
384       }
385       if (g_socket_supports_tcp_user_timeout.load() > 0) {
386         if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
387           gpr_log(GPR_INFO, "Enabling TCP_USER_TIMEOUT with a timeout of %d ms",
388                   timeout);
389         }
390         if (0 != setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout,
391                             sizeof(timeout))) {
392           gpr_log(GPR_ERROR, "setsockopt(TCP_USER_TIMEOUT) %s",
393                   grpc_core::StrError(errno).c_str());
394           return absl::OkStatus();
395         }
396         if (0 != getsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &newval, &len)) {
397           gpr_log(GPR_ERROR, "getsockopt(TCP_USER_TIMEOUT) %s",
398                   grpc_core::StrError(errno).c_str());
399           return absl::OkStatus();
400         }
401         if (newval != timeout) {
402           gpr_log(GPR_INFO,
403                   "Setting TCP_USER_TIMEOUT to value %d ms. Actual "
404                   "TCP_USER_TIMEOUT value is %d ms",
405                   timeout, newval);
406           return absl::OkStatus();
407         }
408       }
409     }
410   } else {
411     if (GRPC_TRACE_FLAG_ENABLED(grpc_tcp_trace)) {
412       gpr_log(GPR_INFO, "TCP_USER_TIMEOUT not supported for this platform");
413     }
414   }
415   return absl::OkStatus();
416 }
417 
418 // set a socket using a grpc_socket_mutator
grpc_set_socket_with_mutator(int fd,grpc_fd_usage usage,grpc_socket_mutator * mutator)419 grpc_error_handle grpc_set_socket_with_mutator(int fd, grpc_fd_usage usage,
420                                                grpc_socket_mutator* mutator) {
421   GPR_ASSERT(mutator);
422   if (!grpc_socket_mutator_mutate_fd(mutator, fd, usage)) {
423     return GRPC_ERROR_CREATE("grpc_socket_mutator failed.");
424   }
425   return absl::OkStatus();
426 }
427 
grpc_apply_socket_mutator_in_args(int fd,grpc_fd_usage usage,const grpc_core::PosixTcpOptions & options)428 grpc_error_handle grpc_apply_socket_mutator_in_args(
429     int fd, grpc_fd_usage usage, const grpc_core::PosixTcpOptions& options) {
430   if (options.socket_mutator == nullptr) {
431     return absl::OkStatus();
432   }
433   return grpc_set_socket_with_mutator(fd, usage, options.socket_mutator);
434 }
435 
436 static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
437 static int g_ipv6_loopback_available;
438 
probe_ipv6_once(void)439 static void probe_ipv6_once(void) {
440   int fd = socket(AF_INET6, SOCK_STREAM, 0);
441   g_ipv6_loopback_available = 0;
442   if (fd < 0) {
443     gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed.");
444   } else {
445     grpc_sockaddr_in6 addr;
446     memset(&addr, 0, sizeof(addr));
447     addr.sin6_family = AF_INET6;
448     addr.sin6_addr.s6_addr[15] = 1;  // [::1]:0
449     if (bind(fd, reinterpret_cast<grpc_sockaddr*>(&addr), sizeof(addr)) == 0) {
450       g_ipv6_loopback_available = 1;
451     } else {
452       gpr_log(GPR_INFO,
453               "Disabling AF_INET6 sockets because ::1 is not available.");
454     }
455     close(fd);
456   }
457 }
458 
grpc_ipv6_loopback_available(void)459 int grpc_ipv6_loopback_available(void) {
460   gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once);
461   return g_ipv6_loopback_available;
462 }
463 
error_for_fd(int fd,const grpc_resolved_address * addr)464 static grpc_error_handle error_for_fd(int fd,
465                                       const grpc_resolved_address* addr) {
466   if (fd >= 0) return absl::OkStatus();
467   auto addr_str = grpc_sockaddr_to_string(addr, false);
468   grpc_error_handle err = grpc_error_set_str(
469       GRPC_OS_ERROR(errno, "socket"),
470       grpc_core::StatusStrProperty::kTargetAddress,
471       addr_str.ok() ? addr_str.value() : addr_str.status().ToString());
472   return err;
473 }
474 
grpc_create_dualstack_socket(const grpc_resolved_address * resolved_addr,int type,int protocol,grpc_dualstack_mode * dsmode,int * newfd)475 grpc_error_handle grpc_create_dualstack_socket(
476     const grpc_resolved_address* resolved_addr, int type, int protocol,
477     grpc_dualstack_mode* dsmode, int* newfd) {
478   return grpc_create_dualstack_socket_using_factory(
479       nullptr, resolved_addr, type, protocol, dsmode, newfd);
480 }
481 
create_socket(grpc_socket_factory * factory,int domain,int type,int protocol)482 static int create_socket(grpc_socket_factory* factory, int domain, int type,
483                          int protocol) {
484   int res = (factory != nullptr)
485                 ? grpc_socket_factory_socket(factory, domain, type, protocol)
486                 : socket(domain, type, protocol);
487   if (res < 0 && errno == EMFILE) {
488     int saved_errno = errno;
489     GRPC_LOG_EVERY_N_SEC(
490         10, GPR_ERROR,
491         "socket(%d, %d, %d) returned %d with error: |%s|. This process "
492         "might not have a sufficient file descriptor limit for the number "
493         "of connections grpc wants to open (which is generally a function of "
494         "the number of grpc channels, the lb policy of each channel, and the "
495         "number of backends each channel is load balancing across).",
496         domain, type, protocol, res, grpc_core::StrError(errno).c_str());
497     errno = saved_errno;
498   }
499   return res;
500 }
501 
grpc_create_dualstack_socket_using_factory(grpc_socket_factory * factory,const grpc_resolved_address * resolved_addr,int type,int protocol,grpc_dualstack_mode * dsmode,int * newfd)502 grpc_error_handle grpc_create_dualstack_socket_using_factory(
503     grpc_socket_factory* factory, const grpc_resolved_address* resolved_addr,
504     int type, int protocol, grpc_dualstack_mode* dsmode, int* newfd) {
505   const grpc_sockaddr* addr =
506       reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr);
507   int family = addr->sa_family;
508   if (family == AF_INET6) {
509     if (grpc_ipv6_loopback_available()) {
510       *newfd = create_socket(factory, family, type, protocol);
511     } else {
512       *newfd = -1;
513       errno = EAFNOSUPPORT;
514     }
515     // Check if we've got a valid dualstack socket.
516     if (*newfd >= 0 && grpc_set_socket_dualstack(*newfd)) {
517       *dsmode = GRPC_DSMODE_DUALSTACK;
518       return absl::OkStatus();
519     }
520     // If this isn't an IPv4 address, then return whatever we've got.
521     if (!grpc_sockaddr_is_v4mapped(resolved_addr, nullptr)) {
522       *dsmode = GRPC_DSMODE_IPV6;
523       return error_for_fd(*newfd, resolved_addr);
524     }
525     // Fall back to AF_INET.
526     if (*newfd >= 0) {
527       close(*newfd);
528     }
529     family = AF_INET;
530   }
531   *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
532   *newfd = create_socket(factory, family, type, protocol);
533   return error_for_fd(*newfd, resolved_addr);
534 }
535 
536 #endif
537