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