1 /*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16 #include <tls/s2n_connection.h>
17
18 #include <utils/s2n_socket.h>
19 #include <utils/s2n_safety.h>
20
21 #include <netinet/tcp.h>
22 #include <netinet/in.h>
23 #include <sys/socket.h>
24 #include <unistd.h>
25
26 #if TCP_CORK
27 #define S2N_CORK TCP_CORK
28 #define S2N_CORK_ON 1
29 #define S2N_CORK_OFF 0
30 #elif TCP_NOPUSH
31 #define S2N_CORK TCP_NOPUSH
32 #define S2N_CORK_ON 1
33 #define S2N_CORK_OFF 0
34 #elif TCP_NODELAY
35 #define S2N_CORK TCP_NODELAY
36 #define S2N_CORK_ON 0
37 #define S2N_CORK_OFF 1
38 #endif
39
s2n_socket_quickack(struct s2n_connection * conn)40 int s2n_socket_quickack(struct s2n_connection *conn)
41 {
42 #ifdef TCP_QUICKACK
43 POSIX_ENSURE_REF(conn);
44 if (!conn->managed_recv_io) {
45 return 0;
46 }
47
48 struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context;
49 POSIX_ENSURE_REF(r_io_ctx);
50 if (r_io_ctx->tcp_quickack_set) {
51 return 0;
52 }
53
54 /* Ignore the return value, if it fails it fails */
55 int optval = 1;
56 if (setsockopt(r_io_ctx->fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == 0) {
57 r_io_ctx->tcp_quickack_set = 1;
58 }
59 #endif
60
61 return 0;
62 }
63
s2n_socket_write_snapshot(struct s2n_connection * conn)64 int s2n_socket_write_snapshot(struct s2n_connection *conn)
65 {
66 #ifdef S2N_CORK
67 socklen_t corklen = sizeof(int);
68 POSIX_ENSURE_REF(conn);
69 struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context;
70 POSIX_ENSURE_REF(w_io_ctx);
71
72 getsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, &corklen);
73 POSIX_ENSURE_EQ(corklen, sizeof(int));
74 w_io_ctx->original_cork_is_set = 1;
75 #endif
76
77 return 0;
78 }
79
s2n_socket_read_snapshot(struct s2n_connection * conn)80 int s2n_socket_read_snapshot(struct s2n_connection *conn)
81 {
82 #ifdef SO_RCVLOWAT
83 socklen_t watlen = sizeof(int);
84 POSIX_ENSURE_REF(conn);
85 struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context;
86 POSIX_ENSURE_REF(r_io_ctx);
87
88 getsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, &watlen);
89 POSIX_ENSURE_EQ(watlen, sizeof(int));
90 r_io_ctx->original_rcvlowat_is_set = 1;
91 #endif
92
93 return 0;
94 }
95
s2n_socket_write_restore(struct s2n_connection * conn)96 int s2n_socket_write_restore(struct s2n_connection *conn)
97 {
98 #ifdef S2N_CORK
99 POSIX_ENSURE_REF(conn);
100 struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context;
101 POSIX_ENSURE_REF(w_io_ctx);
102
103 if (!w_io_ctx->original_cork_is_set) {
104 return 0;
105 }
106 setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, sizeof(w_io_ctx->original_cork_val));
107 w_io_ctx->original_cork_is_set = 0;
108 #endif
109
110 return 0;
111 }
112
s2n_socket_read_restore(struct s2n_connection * conn)113 int s2n_socket_read_restore(struct s2n_connection *conn)
114 {
115 #ifdef SO_RCVLOWAT
116 POSIX_ENSURE_REF(conn);
117 struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context;
118 POSIX_ENSURE_REF(r_io_ctx);
119
120 if (!r_io_ctx->original_rcvlowat_is_set) {
121 return 0;
122 }
123 setsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, sizeof(r_io_ctx->original_rcvlowat_val));
124 r_io_ctx->original_rcvlowat_is_set = 0;
125 #endif
126
127 return 0;
128 }
129
s2n_socket_was_corked(struct s2n_connection * conn)130 int s2n_socket_was_corked(struct s2n_connection *conn)
131 {
132 POSIX_ENSURE_REF(conn);
133 /* If we're not using custom I/O and a send fd has not been set yet, return false*/
134 if (!conn->managed_send_io || !conn->send) {
135 return 0;
136 }
137
138 struct s2n_socket_write_io_context *io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context;
139 POSIX_ENSURE_REF(io_ctx);
140
141 return io_ctx->original_cork_val;
142 }
143
s2n_socket_write_cork(struct s2n_connection * conn)144 int s2n_socket_write_cork(struct s2n_connection *conn)
145 {
146 #ifdef S2N_CORK
147 POSIX_ENSURE_REF(conn);
148 int optval = S2N_CORK_ON;
149
150 struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context;
151 POSIX_ENSURE_REF(w_io_ctx);
152
153 /* Ignore the return value, if it fails it fails */
154 setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval));
155 #endif
156
157 return 0;
158 }
159
s2n_socket_write_uncork(struct s2n_connection * conn)160 int s2n_socket_write_uncork(struct s2n_connection *conn)
161 {
162 #ifdef S2N_CORK
163 POSIX_ENSURE_REF(conn);
164 int optval = S2N_CORK_OFF;
165
166 struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context;
167 POSIX_ENSURE_REF(w_io_ctx);
168
169 /* Ignore the return value, if it fails it fails */
170 setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval));
171 #endif
172
173 return 0;
174 }
175
s2n_socket_set_read_size(struct s2n_connection * conn,int size)176 int s2n_socket_set_read_size(struct s2n_connection *conn, int size)
177 {
178 #ifdef SO_RCVLOWAT
179 POSIX_ENSURE_REF(conn);
180 struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context;
181 POSIX_ENSURE_REF(r_io_ctx);
182
183 setsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &size, sizeof(size));
184 #endif
185
186 return 0;
187 }
188
s2n_socket_read(void * io_context,uint8_t * buf,uint32_t len)189 int s2n_socket_read(void *io_context, uint8_t *buf, uint32_t len)
190 {
191 POSIX_ENSURE_REF(io_context);
192 POSIX_ENSURE_REF(buf);
193 int rfd = ((struct s2n_socket_read_io_context*) io_context)->fd;
194 if (rfd < 0) {
195 errno = EBADF;
196 POSIX_BAIL(S2N_ERR_BAD_FD);
197 }
198
199 /* Clear the quickack flag so we know to reset it */
200 ((struct s2n_socket_read_io_context*) io_context)->tcp_quickack_set = 0;
201
202 /* On success, the number of bytes read is returned. On failure, -1 is
203 * returned and errno is set appropriately. */
204 ssize_t result = read(rfd, buf, len);
205 POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX);
206 return result;
207 }
208
s2n_socket_write(void * io_context,const uint8_t * buf,uint32_t len)209 int s2n_socket_write(void *io_context, const uint8_t *buf, uint32_t len)
210 {
211 POSIX_ENSURE_REF(io_context);
212 POSIX_ENSURE_REF(buf);
213 int wfd = ((struct s2n_socket_write_io_context*) io_context)->fd;
214 if (wfd < 0) {
215 errno = EBADF;
216 POSIX_BAIL(S2N_ERR_BAD_FD);
217 }
218
219 /* On success, the number of bytes written is returned. On failure, -1 is
220 * returned and errno is set appropriately. */
221 ssize_t result = write(wfd, buf, len);
222 POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX);
223 return result;
224 }
225
s2n_socket_is_ipv6(int fd,uint8_t * ipv6)226 int s2n_socket_is_ipv6(int fd, uint8_t *ipv6)
227 {
228 POSIX_ENSURE_REF(ipv6);
229
230 socklen_t len;
231 struct sockaddr_storage addr;
232 len = sizeof (addr);
233 POSIX_GUARD(getpeername(fd, (struct sockaddr*)&addr, &len));
234
235 *ipv6 = 0;
236 if (AF_INET6 == addr.ss_family) {
237 *ipv6 = 1;
238 }
239
240 return 0;
241 }
242