blob: ef160d7ea4c58a427b912bdac9d5f159be647e44 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
3 * Copyright 2014, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "src/core/endpoint/socket_utils.h"
35
nnoble0c475f02014-12-05 15:37:39 -080036#include <arpa/inet.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080037#include <limits.h>
38#include <fcntl.h>
39#include <netinet/in.h>
40#include <netinet/tcp.h>
41#include <stdio.h>
42#include <sys/types.h>
43#include <sys/socket.h>
44#include <unistd.h>
45#include <string.h>
46#include <errno.h>
47
nnoble0c475f02014-12-05 15:37:39 -080048#include <grpc/support/host_port.h>
49#include <grpc/support/string.h>
50#include <grpc/support/log.h>
51#include <grpc/support/port_platform.h>
52
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080053/* set a socket to non blocking mode */
54int grpc_set_socket_nonblocking(int fd, int non_blocking) {
55 int oldflags = fcntl(fd, F_GETFL, 0);
56 if (oldflags < 0) {
57 return 0;
58 }
59
60 if (non_blocking) {
61 oldflags |= O_NONBLOCK;
62 } else {
63 oldflags &= ~O_NONBLOCK;
64 }
65
66 if (fcntl(fd, F_SETFL, oldflags) != 0) {
67 return 0;
68 }
69
70 return 1;
71}
72
73/* set a socket to close on exec */
74int grpc_set_socket_cloexec(int fd, int close_on_exec) {
75 int oldflags = fcntl(fd, F_GETFD, 0);
76 if (oldflags < 0) {
77 return 0;
78 }
79
80 if (close_on_exec) {
81 oldflags |= FD_CLOEXEC;
82 } else {
83 oldflags &= ~FD_CLOEXEC;
84 }
85
86 if (fcntl(fd, F_SETFD, oldflags) != 0) {
87 return 0;
88 }
89
90 return 1;
91}
92
93/* set a socket to reuse old addresses */
94int grpc_set_socket_reuse_addr(int fd, int reuse) {
95 int val = (reuse != 0);
96 int newval;
97 socklen_t intlen = sizeof(newval);
98 return 0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) &&
99 0 == getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen) &&
100 newval == val;
101}
102
103/* disable nagle */
104int grpc_set_socket_low_latency(int fd, int low_latency) {
105 int val = (low_latency != 0);
106 int newval;
107 socklen_t intlen = sizeof(newval);
108 return 0 == setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) &&
109 0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen) &&
110 newval == val;
111}
nnoble0c475f02014-12-05 15:37:39 -0800112
113/* This should be 0 in production, but it may be enabled for testing or
114 debugging purposes, to simulate an environment where IPv6 sockets can't
115 also speak IPv4. */
116int grpc_forbid_dualstack_sockets_for_testing = 0;
117
118static int set_socket_dualstack(int fd) {
119 if (!grpc_forbid_dualstack_sockets_for_testing) {
120 const int off = 0;
121 return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
122 } else {
123 /* Force an IPv6-only socket, for testing purposes. */
124 const int on = 1;
125 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
126 return 0;
127 }
128}
129
130int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
131 int protocol, grpc_dualstack_mode *dsmode) {
132 int family = addr->sa_family;
133 if (family == AF_INET6) {
134 int fd = socket(family, type, protocol);
135 /* Check if we've got a valid dualstack socket. */
136 if (fd >= 0 && set_socket_dualstack(fd)) {
137 *dsmode = GRPC_DSMODE_DUALSTACK;
138 return fd;
139 }
140 /* If this isn't an IPv4 address, then return whatever we've got. */
141 if (!grpc_sockaddr_is_v4mapped(addr, NULL)) {
142 *dsmode = GRPC_DSMODE_IPV6;
143 return fd;
144 }
145 /* Fall back to AF_INET. */
146 if (fd >= 0) {
147 close(fd);
148 }
149 family = AF_INET;
150 }
151 *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
152 return socket(family, type, protocol);
153}
154
155static const gpr_uint8 kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0xff, 0xff};
157
158int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr,
159 struct sockaddr_in *addr4_out) {
160 GPR_ASSERT(addr != (struct sockaddr *)addr4_out);
161 if (addr->sa_family == AF_INET6) {
162 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
163 if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix,
164 sizeof(kV4MappedPrefix)) == 0) {
165 if (addr4_out != NULL) {
166 /* Normalize ::ffff:0.0.0.0/96 to IPv4. */
167 memset(addr4_out, 0, sizeof(*addr4_out));
168 addr4_out->sin_family = AF_INET;
169 /* s6_addr32 would be nice, but it's non-standard. */
170 memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4);
171 addr4_out->sin_port = addr6->sin6_port;
172 }
173 return 1;
174 }
175 }
176 return 0;
177}
178
179int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr,
180 struct sockaddr_in6 *addr6_out) {
181 GPR_ASSERT(addr != (struct sockaddr *)addr6_out);
182 if (addr->sa_family == AF_INET) {
183 const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
184 memset(addr6_out, 0, sizeof(*addr6_out));
185 addr6_out->sin6_family = AF_INET6;
186 memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12);
187 memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4);
188 addr6_out->sin6_port = addr4->sin_port;
189 return 1;
190 }
191 return 0;
192}
193
194int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out) {
195 struct sockaddr_in addr4_normalized;
196 if (grpc_sockaddr_is_v4mapped(addr, &addr4_normalized)) {
197 addr = (struct sockaddr *)&addr4_normalized;
198 }
199 if (addr->sa_family == AF_INET) {
200 /* Check for 0.0.0.0 */
201 const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
202 if (addr4->sin_addr.s_addr != 0) {
203 return 0;
204 }
205 *port_out = ntohs(addr4->sin_port);
206 return 1;
207 } else if (addr->sa_family == AF_INET6) {
208 /* Check for :: */
209 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
210 int i;
211 for (i = 0; i < 16; i++) {
212 if (addr6->sin6_addr.s6_addr[i] != 0) {
213 return 0;
214 }
215 }
216 *port_out = ntohs(addr6->sin6_port);
217 return 1;
218 } else {
219 return 0;
220 }
221}
222
223void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
224 struct sockaddr_in6 *wild6_out) {
225 memset(wild4_out, 0, sizeof(*wild4_out));
226 wild4_out->sin_family = AF_INET;
227 wild4_out->sin_port = htons(port);
228
229 memset(wild6_out, 0, sizeof(*wild6_out));
230 wild6_out->sin6_family = AF_INET6;
231 wild6_out->sin6_port = htons(port);
232}
233
234int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr,
235 int normalize) {
236 const int save_errno = errno;
237 struct sockaddr_in addr_normalized;
238 char ntop_buf[INET6_ADDRSTRLEN];
239 const void *ip = NULL;
240 int port;
241 int ret;
242
243 *out = NULL;
244 if (normalize && grpc_sockaddr_is_v4mapped(addr, &addr_normalized)) {
245 addr = (const struct sockaddr *)&addr_normalized;
246 }
247 if (addr->sa_family == AF_INET) {
248 const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
249 ip = &addr4->sin_addr;
250 port = ntohs(addr4->sin_port);
251 } else if (addr->sa_family == AF_INET6) {
252 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
253 ip = &addr6->sin6_addr;
254 port = ntohs(addr6->sin6_port);
255 }
256 if (ip != NULL &&
257 inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) {
258 ret = gpr_join_host_port(out, ntop_buf, port);
259 } else {
260 ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family);
261 }
262 /* This is probably redundant, but we wouldn't want to log the wrong error. */
263 errno = save_errno;
264 return ret;
265}