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
25 #include <freeradius-devel/libradius.h>
29 /* FIXME: into common RADIUS header? */
30 #define MAX_PACKET_LEN 4096
33 * Open a socket TO the given IP and port.
35 int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
36 fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
39 struct sockaddr_storage salocal;
42 if (!dst_ipaddr) return -1;
44 sockfd = socket(dst_ipaddr->af, SOCK_STREAM, 0);
50 * Allow the caller to bind us to a specific source IP.
52 if (src_ipaddr && (src_ipaddr->af != AF_UNSPEC)) {
53 if (!fr_ipaddr2sockaddr(src_ipaddr, 0, &salocal, &salen)) {
58 if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
59 fr_strerror_printf("Failure binding to IP: %s",
66 if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
72 * FIXME: If EINPROGRESS, then tell the caller that
73 * somehow. The caller can then call connect() when the
76 if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
77 fr_strerror_printf("Failed in connect(): %s", fr_syserror(errno));
86 RADIUS_PACKET *fr_tcp_recv(int sockfd, int flags)
88 RADIUS_PACKET *packet = rad_alloc(NULL, false);
90 if (!packet) return NULL;
92 packet->sockfd = sockfd;
94 if (fr_tcp_read_packet(packet, flags) != 1) {
104 * Receives a packet, assuming that the RADIUS_PACKET structure
105 * has been filled out already.
107 * This ASSUMES that the packet is allocated && fields
110 * This ASSUMES that the socket is marked as O_NONBLOCK, which
111 * the function above does set, if your system supports it.
113 * Calling this function MAY change sockfd,
114 * if src_ipaddr.af == AF_UNSPEC.
116 int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
121 * No data allocated. Read the 4-byte header into
122 * a temporary buffer.
127 len = recv(packet->sockfd, packet->vector + packet->data_len,
128 4 - packet->data_len, 0);
129 if (len == 0) return -2; /* clean close */
132 if ((len < 0) && (errno == ECONNRESET)) { /* forced */
138 fr_strerror_printf("Error receiving packet: %s",
143 packet->data_len += len;
144 if (packet->data_len < 4) { /* want more data */
148 packet_len = (packet->vector[2] << 8) | packet->vector[3];
150 if (packet_len < AUTH_HDR_LEN) {
151 fr_strerror_printf("Discarding packet: Smaller than RFC minimum of 20 bytes");
156 * If the packet is too big, then the socket is bad.
158 if (packet_len > MAX_PACKET_LEN) {
159 fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
163 packet->data = talloc_array(packet, uint8_t, packet_len);
165 fr_strerror_printf("Out of memory");
169 packet->data_len = packet_len;
171 memcpy(packet->data, packet->vector, 4);
175 * Try to read more data.
177 len = recv(packet->sockfd, packet->data + packet->partial,
178 packet->data_len - packet->partial, 0);
179 if (len == 0) return -2; /* clean close */
182 if ((len < 0) && (errno == ECONNRESET)) { /* forced */
188 fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
192 packet->partial += len;
194 if (packet->partial < packet->data_len) {
199 * See if it's a well-formed RADIUS packet.
201 if (!rad_packet_ok(packet, flags, NULL)) {
206 * Explicitly set the VP list to empty.
211 char ip_buf[128], buffer[256];
213 if (packet->src_ipaddr.af != AF_UNSPEC) {
214 inet_ntop(packet->src_ipaddr.af,
215 &packet->src_ipaddr.ipaddr,
216 ip_buf, sizeof(ip_buf));
217 snprintf(buffer, sizeof(buffer), "host %s port %d",
218 ip_buf, packet->src_port);
220 snprintf(buffer, sizeof(buffer), "socket %d",
225 if (is_radius_code(packet->code)) {
226 DEBUG("Received %s packet from %s",
227 fr_packet_codes[packet->code], buffer);
229 DEBUG("Received packet from %s code %d",
230 buffer, packet->code);
232 DEBUG(", id=%d, length=%zu\n", packet->id, packet->data_len);
235 return 1; /* done reading the packet */
238 #endif /* WITH_TCP */