2 * tcp.c TCP-specific functions.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright (C) 2009 Dante http://dante.net
23 #include <freeradius-devel/ident.h>
26 #include <freeradius-devel/libradius.h>
27 #include <freeradius-devel/tcp.h>
31 /* FIXME: into common RADIUS header? */
32 #define MAX_PACKET_LEN 4096
35 * Open a socket on the given IP and port.
37 int fr_tcp_socket(fr_ipaddr_t *ipaddr, int port)
41 struct sockaddr_storage salocal;
44 if ((port < 0) || (port > 65535)) {
45 fr_strerror_printf("Port %d is out of allowed bounds", port);
49 sockfd = socket(ipaddr->af, SOCK_STREAM, 0);
54 if (fr_nonblock(sockfd) < 0) {
59 if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
64 #ifdef HAVE_STRUCT_SOCKADDR_IN6
65 if (ipaddr->af == AF_INET6) {
67 * Listening on '::' does NOT get you IPv4 to
68 * IPv6 mapping. You've got to listen on an IPv4
69 * address, too. This makes the rest of the server
70 * design a little simpler.
74 if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
75 if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
76 (char *)&on, sizeof(on)) < 0) {
77 fr_strerror_printf("Failed in setsockopt(): %s",
83 #endif /* IPV6_V6ONLY */
85 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
87 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
88 fr_strerror_printf("Failed in setsockopt(): %s", strerror(errno));
93 if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
94 fr_strerror_printf("Failed in bind(): %s", strerror(errno));
99 if (listen(sockfd, 8) < 0) {
100 fr_strerror_printf("Failed in listen(): %s", strerror(errno));
110 * Open a socket TO the given IP and port.
112 int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
113 fr_ipaddr_t *dst_ipaddr, int dst_port)
116 struct sockaddr_storage salocal;
119 if ((dst_port < 0) || (dst_port > 65535)) {
120 fr_strerror_printf("Port %d is out of allowed bounds",
125 if (!dst_ipaddr) return -1;
127 sockfd = socket(dst_ipaddr->af, SOCK_STREAM, 0);
137 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0) {
138 fr_strerror_printf("Failure getting socket flags: %s",
145 if( fcntl(sockfd, F_SETFL, flags) < 0) {
146 fr_strerror_printf("Failure setting socket flags: %s",
155 * Allow the caller to bind us to a specific source IP.
157 if (src_ipaddr && (src_ipaddr->af != AF_UNSPEC)) {
158 if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &salocal, &salen)) {
163 if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
164 fr_strerror_printf("Failure binding to IP: %s",
171 if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
177 * FIXME: If EINPROGRESS, then tell the caller that
178 * somehow. The caller can then call connect() when the
181 if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
182 fr_strerror_printf("Failed in connect(): %s", strerror(errno));
191 RADIUS_PACKET *fr_tcp_recv(int sockfd, int flags)
193 RADIUS_PACKET *packet = rad_alloc(NULL, 0);
195 if (!packet) return NULL;
197 packet->sockfd = sockfd;
199 if (fr_tcp_read_packet(packet, flags) != 1) {
209 * Receives a packet, assuming that the RADIUS_PACKET structure
210 * has been filled out already.
212 * This ASSUMES that the packet is allocated && fields
215 * This ASSUMES that the socket is marked as O_NONBLOCK, which
216 * the function above does set, if your system supports it.
218 * Calling this function MAY change sockfd,
219 * if src_ipaddr.af == AF_UNSPEC.
221 int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
226 * No data allocated. Read the 4-byte header into
227 * a temporary buffer.
232 len = recv(packet->sockfd, packet->vector + packet->data_len,
233 4 - packet->data_len, 0);
234 if (len == 0) return -2; /* clean close */
237 if ((len < 0) && (errno == ECONNRESET)) { /* forced */
243 fr_strerror_printf("Error receiving packet: %s",
248 packet->data_len += len;
249 if (packet->data_len < 4) { /* want more data */
253 packet_len = (packet->vector[2] << 8) | packet->vector[3];
255 if (packet_len < AUTH_HDR_LEN) {
256 fr_strerror_printf("Discarding packet: Smaller than RFC minimum of 20 bytes.");
261 * If the packet is too big, then the socket is bad.
263 if (packet_len > MAX_PACKET_LEN) {
264 fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
268 packet->data = talloc_array(packet, uint8_t, packet_len);
270 fr_strerror_printf("Out of memory");
274 packet->data_len = packet_len;
276 memcpy(packet->data, packet->vector, 4);
280 * Try to read more data.
282 len = recv(packet->sockfd, packet->data + packet->partial,
283 packet->data_len - packet->partial, 0);
284 if (len == 0) return -2; /* clean close */
287 if ((len < 0) && (errno == ECONNRESET)) { /* forced */
293 fr_strerror_printf("Error receiving packet: %s", strerror(errno));
297 packet->partial += len;
299 if (packet->partial < packet->data_len) {
304 * See if it's a well-formed RADIUS packet.
306 if (!rad_packet_ok(packet, flags)) {
311 * Explicitly set the VP list to empty.
316 char ip_buf[128], buffer[256];
318 if (packet->src_ipaddr.af != AF_UNSPEC) {
319 inet_ntop(packet->src_ipaddr.af,
320 &packet->src_ipaddr.ipaddr,
321 ip_buf, sizeof(ip_buf));
322 snprintf(buffer, sizeof(buffer), "host %s port %d",
323 ip_buf, packet->src_port);
325 snprintf(buffer, sizeof(buffer), "socket %d",
330 if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
331 DEBUG("rad_recv: %s packet from %s",
332 fr_packet_codes[packet->code], buffer);
334 DEBUG("rad_recv: Packet from %s code=%d",
335 buffer, packet->code);
337 DEBUG(", id=%d, length=%zu\n", packet->id, packet->data_len);
340 return 1; /* done reading the packet */
343 RADIUS_PACKET *fr_tcp_accept(int sockfd)
347 RADIUS_PACKET *packet;
348 struct sockaddr_storage src;
352 newfd = accept(sockfd, (struct sockaddr *) &src, &salen);
355 * Non-blocking sockets must handle this.
358 if (errno == EWOULDBLOCK) {
359 packet = rad_alloc(NULL, 0);
360 if (!packet) return NULL;
362 packet->sockfd = sockfd;
363 packet->src_ipaddr.af = AF_UNSPEC;
371 packet = rad_alloc(NULL, 0);
377 if (src.ss_family == AF_INET) {
378 struct sockaddr_in s4;
380 memcpy(&s4, &src, sizeof(s4));
381 packet->src_ipaddr.af = AF_INET;
382 packet->src_ipaddr.ipaddr.ip4addr = s4.sin_addr;
383 packet->src_port = ntohs(s4.sin_port);
385 #ifdef HAVE_STRUCT_SOCKADDR_IN6
386 } else if (src.ss_family == AF_INET6) {
387 struct sockaddr_in6 s6;
389 memcpy(&s6, &src, sizeof(s6));
390 packet->src_ipaddr.af = AF_INET6;
391 packet->src_ipaddr.ipaddr.ip6addr = s6.sin6_addr;
392 packet->src_port = ntohs(s6.sin6_port);
401 packet->sockfd = newfd;
404 * Note: Caller has to set dst_ipaddr && dst_port.
411 * Writes a packet, assuming it's already been encoded.
413 * It returns the number of bytes written, which MAY be less than
414 * the packet size (data_len). It is the caller's responsibility
415 * to check the return code, and to schedule writes again.
417 ssize_t fr_tcp_write_packet(RADIUS_PACKET *packet)
421 if (!packet || !packet->data) return 0;
423 if (packet->partial >= packet->data_len) return packet->data_len;
425 rcode = write(packet->sockfd, packet->data + packet->partial,
426 packet->data_len - packet->partial);
427 if (rcode < 0) return packet->partial; /* ignore most errors */
429 packet->partial += rcode;
431 return packet->partial;
433 #endif /* WITH_TCP */