1 /*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17 /**
18 * $Id: 3c88b78f9f00b188267ab16ad88a71d1663a5ab2 $
19 * @file socket.c
20 * @brief Functions for establishing and managing low level sockets.
21 *
22 * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23 * @author Alan DeKok <aland@freeradius.org>
24 *
25 * @copyright 2015 The FreeRADIUS project
26 */
27 #include <freeradius-devel/libradius.h>
28
29 #ifdef HAVE_SYS_UN_H
30 # include <sys/un.h>
31 # ifndef SUN_LEN
32 # define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
33 # endif
34
35 /** Open a Unix socket
36 *
37 * @note If the file doesn't exist then errno will be set to ENOENT.
38 *
39 * The following code demonstrates using this function with a connection timeout:
40 @code {.c}
41 sockfd = fr_socket_client_unix(path, true);
42 if (sockfd < 0) {
43 fr_perror();
44 exit(1);
45 }
46 if ((errno == EINPROGRESS) && (fr_socket_wait_for_connect(sockfd, timeout) < 0)) {
47 error:
48 fr_perror();
49 close(sockfd);
50 goto error;
51 }
52 //Optionally, if blocking operation is required
53 if (fr_blocking(sockfd) < 0) goto error;
54 @endcode
55 *
56 * @param path to the file bound to the unix socket.
57 * @param async Whether to set the socket to nonblocking, allowing use of
58 * #fr_socket_wait_for_connect.
59 * @return socket FD on success, -1 on error.
60 */
fr_socket_client_unix(char const * path,bool async)61 int fr_socket_client_unix(char const *path, bool async)
62 {
63 int sockfd = -1;
64 size_t len;
65 socklen_t socklen;
66 struct sockaddr_un saremote;
67
68 len = strlen(path);
69 if (len >= sizeof(saremote.sun_path)) {
70 fr_strerror_printf("Path too long, maximum length is %zu", sizeof(saremote.sun_path) - 1);
71 errno = EINVAL;
72 return -1;
73 }
74
75 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
76 if (sockfd < 0) {
77 fr_strerror_printf("Failed creating UNIX socket: %s", fr_syserror(errno));
78 return -1;
79 }
80
81 if (async && (fr_nonblock(sockfd) < 0)) {
82 close(sockfd);
83 return -1;
84 }
85
86 saremote.sun_family = AF_UNIX;
87 memcpy(saremote.sun_path, path, len + 1); /* SUN_LEN does strlen */
88
89 socklen = SUN_LEN(&saremote);
90
91 /*
92 * Although we ignore SIGPIPE, some operating systems
93 * like BSD and OSX ignore the ignoring.
94 *
95 * Fortunately, those operating systems usually support
96 * SO_NOSIGPIPE, to prevent them raising the signal in
97 * the first place.
98 */
99 #ifdef SO_NOSIGPIPE
100 {
101 int set = 1;
102
103 setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
104 }
105 #endif
106
107 if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
108 /*
109 * POSIX says the only time we will get this,
110 * is if the socket has been marked as
111 * nonblocking. This is not an error, the caller
112 * must check the state of errno, and wait for
113 * the connection to complete.
114 */
115 if (errno == EINPROGRESS) return sockfd;
116
117 close(sockfd);
118 fr_strerror_printf("Failed connecting to %s: %s", path, fr_syserror(errno));
119
120 return -1;
121 }
122 return sockfd;
123 }
124 #else
fr_socket_client_unix(UNUSED char const * path,UNUSED bool async)125 int fr_socket_client_unix(UNUSED char const *path, UNUSED bool async)
126 {
127 fprintf(stderr, "Unix domain sockets not supported on this system");
128 return -1;
129 }
130 #endif /* WITH_SYS_UN_H */
131
132 /** Establish a connected TCP socket
133 *
134 * The following code demonstrates using this function with a connection timeout:
135 @code {.c}
136 sockfd = fr_socket_client_tcp(NULL, ipaddr, port, true);
137 if (sockfd < 0) {
138 fr_perror();
139 exit(1);
140 }
141 if ((errno == EINPROGRESS) && (fr_socket_wait_for_connect(sockfd, timeout) < 0)) {
142 error:
143 fr_perror();
144 close(sockfd);
145 goto error;
146 }
147 //Optionally, if blocking operation is required
148 if (fr_blocking(sockfd) < 0) goto error;
149 @endcode
150 *
151 * @param src_ipaddr to bind socket to, may be NULL if socket is not bound to any specific
152 * address.
153 * @param dst_ipaddr Where to connect to.
154 * @param dst_port Where to connect to.
155 * @param async Whether to set the socket to nonblocking, allowing use of
156 * #fr_socket_wait_for_connect.
157 * @return FD on success, -1 on failure.
158 */
fr_socket_client_tcp(fr_ipaddr_t * src_ipaddr,fr_ipaddr_t * dst_ipaddr,uint16_t dst_port,bool async)159 int fr_socket_client_tcp(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, bool async)
160 {
161 int sockfd;
162 struct sockaddr_storage salocal;
163 socklen_t salen;
164
165 if (!dst_ipaddr) return -1;
166
167 sockfd = socket(dst_ipaddr->af, SOCK_STREAM, 0);
168 if (sockfd < 0) {
169 fr_strerror_printf("Error creating TCP socket: %s", fr_syserror(errno));
170 return sockfd;
171 }
172
173 if (async && (fr_nonblock(sockfd) < 0)) {
174 close(sockfd);
175 return -1;
176 }
177
178 /*
179 * Allow the caller to bind us to a specific source IP.
180 */
181 if (src_ipaddr && (src_ipaddr->af != AF_UNSPEC)) {
182 if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &salocal, &salen)) {
183 close(sockfd);
184 return -1;
185 }
186
187 if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
188 fr_strerror_printf("Failure binding to IP: %s", fr_syserror(errno));
189 close(sockfd);
190 return -1;
191 }
192 }
193
194 if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
195 close(sockfd);
196 return -1;
197 }
198
199 /*
200 * Although we ignore SIGPIPE, some operating systems
201 * like BSD and OSX ignore the ignoring.
202 *
203 * Fortunately, those operating systems usually support
204 * SO_NOSIGPIPE, to prevent them raising the signal in
205 * the first place.
206 */
207 #ifdef SO_NOSIGPIPE
208 {
209 int set = 1;
210
211 setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
212 }
213 #endif
214
215 if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
216 /*
217 * POSIX says the only time we will get this,
218 * is if the socket has been marked as
219 * nonblocking. This is not an error, the caller
220 * must check the state of errno, and wait for
221 * the connection to complete.
222 */
223 if (errno == EINPROGRESS) return sockfd;
224
225 fr_strerror_printf("Failed connecting socket: %s", fr_syserror(errno));
226 close(sockfd);
227 return -1;
228 }
229
230 return sockfd;
231 }
232
233 /** Establish a connected UDP socket
234 *
235 * Connected UDP sockets can be used with write(), unlike unconnected sockets
236 * which must be used with sendto and recvfrom.
237 *
238 * The following code demonstrates using this function with a connection timeout:
239 @code {.c}
240 sockfd = fr_socket_client_udp(NULL, ipaddr, port, true);
241 if (sockfd < 0) {
242 fr_perror();
243 exit(1);
244 }
245 if ((errno == EINPROGRESS) && (fr_socket_wait_for_connect(sockfd, timeout) < 0)) {
246 error:
247 fr_perror();
248 close(sockfd);
249 goto error;
250 }
251 //Optionally, if blocking operation is required
252 if (fr_blocking(sockfd) < 0) goto error;
253 @endcode
254 *
255 * @param src_ipaddr to bind socket to, may be NULL if socket is not bound to any specific
256 * address.
257 * @param dst_ipaddr Where to send datagrams.
258 * @param dst_port Where to send datagrams.
259 * @param async Whether to set the socket to nonblocking, allowing use of
260 * #fr_socket_wait_for_connect.
261 * @return FD on success, -1 on failure.
262 */
fr_socket_client_udp(fr_ipaddr_t * src_ipaddr,fr_ipaddr_t * dst_ipaddr,uint16_t dst_port,bool async)263 int fr_socket_client_udp(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, bool async)
264 {
265 int sockfd;
266 struct sockaddr_storage salocal;
267 socklen_t salen;
268
269 if (!dst_ipaddr) return -1;
270
271 sockfd = socket(dst_ipaddr->af, SOCK_DGRAM, 0);
272 if (sockfd < 0) {
273 fr_strerror_printf("Error creating UDP socket: %s", fr_syserror(errno));
274 return sockfd;
275 }
276
277 if (async && (fr_nonblock(sockfd) < 0)) {
278 close(sockfd);
279 return -1;
280 }
281
282 /*
283 * Allow the caller to bind us to a specific source IP.
284 */
285 if (src_ipaddr && (src_ipaddr->af != AF_UNSPEC)) {
286 if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &salocal, &salen)) {
287 close(sockfd);
288 return -1;
289 }
290
291 if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
292 fr_strerror_printf("Failure binding to IP: %s", fr_syserror(errno));
293 close(sockfd);
294 return -1;
295 }
296 }
297
298 if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
299 close(sockfd);
300 return -1;
301 }
302
303 /*
304 * Although we ignore SIGPIPE, some operating systems
305 * like BSD and OSX ignore the ignoring.
306 *
307 * Fortunately, those operating systems usually support
308 * SO_NOSIGPIPE, to prevent them raising the signal in
309 * the first place.
310 */
311 #ifdef SO_NOSIGPIPE
312 {
313 int set = 1;
314
315 setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
316 }
317 #endif
318
319 if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
320 /*
321 * POSIX says the only time we will get this,
322 * is if the socket has been marked as
323 * nonblocking. This is not an error, the caller
324 * must check the state of errno, and wait for
325 * the connection to complete.
326 */
327 if (errno == EINPROGRESS) return sockfd;
328
329 fr_strerror_printf("Failed connecting socket: %s", fr_syserror(errno));
330 close(sockfd);
331 return -1;
332 }
333
334 return sockfd;
335 }
336
337 /** Wait for a socket to be connected, with an optional timeout
338 *
339 * @note On error the caller is expected to ``close(sockfd)``.
340 *
341 * @param sockfd the socket to wait on.
342 * @param timeout How long to wait for socket to open.
343 * @return 0 on success, -1 on connection error, -2 on timeout, -3 on select error.
344 */
fr_socket_wait_for_connect(int sockfd,struct timeval * timeout)345 int fr_socket_wait_for_connect(int sockfd, struct timeval *timeout)
346 {
347 int ret;
348 fd_set error_set;
349 fd_set write_set; /* POSIX says sockets are open when they become writeable */
350
351 FD_ZERO(&error_set);
352 FD_ZERO(&write_set);
353
354 FD_SET(sockfd, &error_set);
355 FD_SET(sockfd, &write_set);
356
357 /* Don't let signals mess up the select */
358 do {
359 ret = select(sockfd + 1, NULL, &write_set, &error_set, timeout);
360 } while ((ret == -1) && (errno == EINTR));
361
362 switch (ret) {
363 case 1: /* ok (maybe) */
364 {
365 int error;
366 socklen_t socklen = sizeof(error);
367
368 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&error, &socklen)) {
369 fr_strerror_printf("Failed connecting socket: %s", fr_syserror(errno));
370 return -1;
371 }
372
373 if (FD_ISSET(sockfd, &error_set)) {
374 fr_strerror_printf("Failed connecting socket: Unknown error");
375 return -1;
376 }
377 }
378 return 0;
379
380 case 0: /* timeout */
381 if (!fr_assert(timeout)) return -1;
382 fr_strerror_printf("Connection timed out after %" PRIu64"ms",
383 (timeout->tv_sec * (uint64_t)1000) + (timeout->tv_usec / 1000));
384 return -2;
385
386 case -1: /* select error */
387 fr_strerror_printf("Failed waiting for connection: %s", fr_syserror(errno));
388 return -3;
389
390 default:
391 fr_assert(0);
392 return -1;
393 }
394 }
395