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