Line data Source code
1 : /*
2 : SSSD
3 :
4 : Socket utils
5 :
6 : Copyright (C) Simo Sorce <ssorce@redhat.com> 2016
7 : Copyright (C) Sumit Bose <sbose@redhat.com> 2009
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "config.h"
24 :
25 : #include <stdlib.h>
26 : #include <unistd.h>
27 : #include <fcntl.h>
28 : #include <sys/socket.h>
29 : #include <netinet/in.h>
30 : #include <netinet/tcp.h>
31 :
32 : #include "util/util.h"
33 :
34 :
35 0 : static errno_t set_fcntl_flags(int fd, int fd_flags, int fl_flags)
36 : {
37 : int ret;
38 : int cur_flags;
39 :
40 0 : ret = fcntl(fd, F_GETFD, 0);
41 0 : if (ret == -1) {
42 0 : ret = errno;
43 0 : DEBUG(SSSDBG_CRIT_FAILURE,
44 : "fcntl F_GETFD failed [%d][%s].\n", ret, strerror(ret));
45 0 : return ret;
46 : }
47 0 : cur_flags = ret;
48 :
49 0 : ret = fcntl(fd, F_SETFD, cur_flags | fd_flags);
50 0 : if (ret == -1) {
51 0 : ret = errno;
52 0 : DEBUG(SSSDBG_CRIT_FAILURE,
53 : "fcntl F_SETFD failed [%d][%s].\n", ret, strerror(ret));
54 0 : return ret;
55 : }
56 :
57 0 : ret = fcntl(fd, F_GETFL, 0);
58 0 : if (ret == -1) {
59 0 : ret = errno;
60 0 : DEBUG(SSSDBG_CRIT_FAILURE,
61 : "fcntl F_GETFD failed [%d][%s].\n", ret, strerror(ret));
62 0 : return ret;
63 : }
64 0 : cur_flags = ret;
65 :
66 0 : ret = fcntl(fd, F_SETFL, cur_flags | fl_flags);
67 0 : if (ret == -1) {
68 0 : ret = errno;
69 0 : DEBUG(SSSDBG_CRIT_FAILURE,
70 : "fcntl F_SETFD failed [%d][%s].\n", ret, strerror(ret));
71 0 : return ret;
72 : }
73 :
74 0 : return EOK;
75 : }
76 :
77 0 : static errno_t set_fd_common_opts(int fd)
78 : {
79 0 : int dummy = 1;
80 : int ret;
81 :
82 : /* SO_KEEPALIVE and TCP_NODELAY are set by OpenLDAP client libraries but
83 : * failures are ignored.*/
84 0 : ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &dummy, sizeof(dummy));
85 0 : if (ret != 0) {
86 0 : ret = errno;
87 0 : DEBUG(SSSDBG_FUNC_DATA,
88 : "setsockopt SO_KEEPALIVE failed.[%d][%s].\n", ret,
89 : strerror(ret));
90 : }
91 :
92 0 : ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &dummy, sizeof(dummy));
93 0 : if (ret != 0) {
94 0 : ret = errno;
95 0 : DEBUG(SSSDBG_FUNC_DATA,
96 : "setsockopt TCP_NODELAY failed.[%d][%s].\n", ret,
97 : strerror(ret));
98 : }
99 :
100 0 : return EOK;
101 : }
102 :
103 :
104 : struct sssd_async_connect_state {
105 : struct tevent_fd *fde;
106 : int fd;
107 : socklen_t addr_len;
108 : struct sockaddr_storage addr;
109 : };
110 :
111 : static void sssd_async_connect_done(struct tevent_context *ev,
112 : struct tevent_fd *fde, uint16_t flags,
113 : void *priv);
114 :
115 0 : struct tevent_req *sssd_async_connect_send(TALLOC_CTX *mem_ctx,
116 : struct tevent_context *ev,
117 : int fd,
118 : const struct sockaddr *addr,
119 : socklen_t addr_len)
120 : {
121 : struct tevent_req *req;
122 : struct sssd_async_connect_state *state;
123 : int ret;
124 :
125 0 : req = tevent_req_create(mem_ctx, &state,
126 : struct sssd_async_connect_state);
127 0 : if (req == NULL) {
128 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
129 0 : return NULL;
130 : }
131 :
132 0 : state->fd = fd;
133 0 : state->addr_len = addr_len;
134 0 : memcpy(&state->addr, addr, addr_len);
135 :
136 0 : ret = connect(fd, addr, addr_len);
137 0 : if (ret == EOK) {
138 0 : goto done;
139 : }
140 :
141 0 : ret = errno;
142 0 : switch (ret) {
143 : case EINPROGRESS:
144 : case EINTR:
145 :
146 : /* Despite the connect() man page says waiting on a non-blocking
147 : * connect should be done by checking for writability, we need to check
148 : * also for readability.
149 : * With TEVENT_FD_READ, connect fails much faster in offline mode with
150 : * errno 113/No route to host.
151 : */
152 0 : state->fde = tevent_add_fd(ev, state, fd,
153 : TEVENT_FD_READ | TEVENT_FD_WRITE,
154 : sssd_async_connect_done, req);
155 0 : if (state->fde == NULL) {
156 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd failed.\n");
157 0 : ret = ENOMEM;
158 0 : goto done;
159 : }
160 :
161 0 : return req;
162 :
163 : default:
164 0 : DEBUG(SSSDBG_CRIT_FAILURE,
165 : "connect failed [%d][%s].\n", ret, strerror(ret));
166 : }
167 :
168 : done:
169 0 : if (ret == EOK) {
170 0 : tevent_req_done(req);
171 : } else {
172 0 : tevent_req_error(req, ret);
173 : }
174 :
175 0 : tevent_req_post(req, ev);
176 0 : return req;
177 : }
178 :
179 0 : static void sssd_async_connect_done(struct tevent_context *ev,
180 : struct tevent_fd *fde, uint16_t flags,
181 : void *priv)
182 : {
183 0 : struct tevent_req *req = talloc_get_type(priv, struct tevent_req);
184 0 : struct sssd_async_connect_state *state =
185 0 : tevent_req_data(req, struct sssd_async_connect_state);
186 : int ret;
187 :
188 0 : errno = 0;
189 0 : ret = connect(state->fd, (struct sockaddr *) &state->addr,
190 : state->addr_len);
191 0 : if (ret == -1) {
192 0 : ret = errno;
193 0 : if (ret == EALREADY || ret == EINPROGRESS || ret == EINTR) {
194 0 : return; /* Try again later */
195 : }
196 : }
197 :
198 0 : talloc_zfree(fde);
199 :
200 0 : if (ret == EOK) {
201 0 : tevent_req_done(req);
202 : } else {
203 0 : ret = errno;
204 0 : DEBUG(SSSDBG_CRIT_FAILURE,
205 : "connect failed [%d][%s].\n", ret, strerror(ret));
206 0 : tevent_req_error(req, ret);
207 : }
208 : }
209 :
210 0 : int sssd_async_connect_recv(struct tevent_req *req)
211 : {
212 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
213 :
214 0 : return EOK;
215 : }
216 :
217 :
218 0 : static void sssd_async_connect_timeout(struct tevent_context *ev,
219 : struct tevent_timer *te,
220 : struct timeval tv, void *pvt)
221 : {
222 : struct tevent_req *connection_request;
223 :
224 0 : DEBUG(SSSDBG_CONF_SETTINGS, "The connection timed out\n");
225 :
226 0 : connection_request = talloc_get_type(pvt, struct tevent_req);
227 0 : tevent_req_error(connection_request, ETIMEDOUT);
228 0 : }
229 :
230 :
231 : struct sssd_async_socket_state {
232 : struct tevent_timer *connect_timeout;
233 : int sd;
234 : };
235 :
236 : static int sssd_async_socket_state_destructor(void *data);
237 : static void sssd_async_socket_init_done(struct tevent_req *subreq);
238 :
239 0 : struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx,
240 : struct tevent_context *ev,
241 : struct sockaddr_storage *addr,
242 : socklen_t addr_len, int timeout)
243 : {
244 : struct sssd_async_socket_state *state;
245 : struct tevent_req *req, *subreq;
246 : struct timeval tv;
247 : int ret;
248 :
249 0 : req = tevent_req_create(mem_ctx, &state, struct sssd_async_socket_state);
250 0 : if (req == NULL) {
251 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
252 0 : return NULL;
253 : }
254 0 : state->sd = -1;
255 :
256 0 : talloc_set_destructor((TALLOC_CTX *)state,
257 : sssd_async_socket_state_destructor);
258 :
259 0 : state->sd = socket(addr->ss_family, SOCK_STREAM, 0);
260 0 : if (state->sd == -1) {
261 0 : ret = errno;
262 0 : DEBUG(SSSDBG_CRIT_FAILURE,
263 : "socket failed [%d][%s].\n", ret, strerror(ret));
264 0 : goto fail;
265 : }
266 :
267 0 : ret = set_fd_common_opts(state->sd);
268 0 : if (ret != EOK) {
269 0 : DEBUG(SSSDBG_CRIT_FAILURE, "set_fd_common_opts failed.\n");
270 0 : goto fail;
271 : }
272 :
273 0 : ret = set_fcntl_flags(state->sd, FD_CLOEXEC, O_NONBLOCK);
274 0 : if (ret != EOK) {
275 0 : DEBUG(SSSDBG_CRIT_FAILURE, "settting fd flags failed.\n");
276 0 : goto fail;
277 : }
278 :
279 0 : DEBUG(SSSDBG_TRACE_ALL,
280 : "Using file descriptor [%d] for the connection.\n", state->sd);
281 :
282 0 : subreq = sssd_async_connect_send(state, ev, state->sd,
283 : (struct sockaddr *) addr, addr_len);
284 0 : if (subreq == NULL) {
285 0 : ret = ENOMEM;
286 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sssd_async_connect_send failed.\n");
287 0 : goto fail;
288 : }
289 :
290 0 : DEBUG(SSSDBG_TRACE_FUNC,
291 : "Setting %d seconds timeout for connecting\n", timeout);
292 0 : tv = tevent_timeval_current_ofs(timeout, 0);
293 :
294 0 : state->connect_timeout = tevent_add_timer(ev, subreq, tv,
295 : sssd_async_connect_timeout,
296 : subreq);
297 0 : if (state->connect_timeout == NULL) {
298 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n");
299 0 : ret = ENOMEM;
300 0 : goto fail;
301 : }
302 :
303 0 : tevent_req_set_callback(subreq, sssd_async_socket_init_done, req);
304 0 : return req;
305 :
306 : fail:
307 0 : tevent_req_error(req, ret);
308 0 : tevent_req_post(req, ev);
309 0 : return req;
310 : }
311 :
312 0 : static void sssd_async_socket_init_done(struct tevent_req *subreq)
313 : {
314 0 : struct tevent_req *req =
315 0 : tevent_req_callback_data(subreq, struct tevent_req);
316 0 : struct sssd_async_socket_state *state =
317 0 : tevent_req_data(req, struct sssd_async_socket_state);
318 : int ret;
319 :
320 : /* kill the timeout handler now that we got a reply */
321 0 : talloc_zfree(state->connect_timeout);
322 :
323 0 : ret = sssd_async_connect_recv(subreq);
324 0 : talloc_zfree(subreq);
325 0 : if (ret != EOK) {
326 0 : DEBUG(SSSDBG_CRIT_FAILURE,
327 : "sdap_async_sys_connect request failed: [%d]: %s.\n",
328 : ret, sss_strerror(ret));
329 0 : goto fail;
330 : }
331 :
332 0 : tevent_req_done(req);
333 0 : return;
334 :
335 : fail:
336 0 : tevent_req_error(req, ret);
337 : }
338 :
339 0 : int sssd_async_socket_init_recv(struct tevent_req *req, int *sd)
340 : {
341 0 : struct sssd_async_socket_state *state =
342 0 : tevent_req_data(req, struct sssd_async_socket_state);
343 :
344 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
345 :
346 : /* steal the sd and neutralize destructor actions */
347 0 : *sd = state->sd;
348 0 : state->sd = -1;
349 :
350 0 : return EOK;
351 : }
352 :
353 0 : static int sssd_async_socket_state_destructor(void *data)
354 : {
355 0 : struct sssd_async_socket_state *state =
356 : talloc_get_type(data, struct sssd_async_socket_state);
357 :
358 0 : if (state->sd != -1) {
359 0 : DEBUG(SSSDBG_TRACE_FUNC, "closing socket [%d]\n", state->sd);
360 0 : close(state->sd);
361 0 : state->sd = -1;
362 : }
363 :
364 0 : return 0;
365 : }
|