Remove redundant file from freeradius-abfab list.
[freeradius.git] / src / lib / tcp.c
1 /*
2  * tcp.c        TCP-specific functions.
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright (C) 2009 Dante http://dante.net
21  */
22
23 RCSID("$Id$")
24
25 #include        <freeradius-devel/libradius.h>
26
27 #ifdef WITH_TCP
28
29 /* FIXME: into common RADIUS header? */
30 #define MAX_PACKET_LEN 4096
31
32 RADIUS_PACKET *fr_tcp_recv(int sockfd, int flags)
33 {
34         RADIUS_PACKET *packet = rad_alloc(NULL, false);
35
36         if (!packet) return NULL;
37
38         packet->sockfd = sockfd;
39
40         if (fr_tcp_read_packet(packet, flags) != 1) {
41                 rad_free(&packet);
42                 return NULL;
43         }
44
45         return packet;
46 }
47
48
49 /*
50  *      Receives a packet, assuming that the RADIUS_PACKET structure
51  *      has been filled out already.
52  *
53  *      This ASSUMES that the packet is allocated && fields
54  *      initialized.
55  *
56  *      This ASSUMES that the socket is marked as O_NONBLOCK, which
57  *      the function above does set, if your system supports it.
58  *
59  *      Calling this function MAY change sockfd,
60  *      if src_ipaddr.af == AF_UNSPEC.
61  */
62 int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
63 {
64         ssize_t len;
65
66         /*
67          *      No data allocated.  Read the 4-byte header into
68          *      a temporary buffer.
69          */
70         if (!packet->data) {
71                 int packet_len;
72
73                 len = recv(packet->sockfd, packet->vector + packet->data_len,
74                            4 - packet->data_len, 0);
75                 if (len == 0) return -2; /* clean close */
76
77 #ifdef ECONNRESET
78                 if ((len < 0) && (errno == ECONNRESET)) { /* forced */
79                         return -2;
80                 }
81 #endif
82
83                 if (len < 0) {
84                         fr_strerror_printf("Error receiving packet: %s",
85                                    fr_syserror(errno));
86                         return -1;
87                 }
88
89                 packet->data_len += len;
90                 if (packet->data_len < 4) { /* want more data */
91                         return 0;
92                 }
93
94                 packet_len = (packet->vector[2] << 8) | packet->vector[3];
95
96                 if (packet_len < RADIUS_HDR_LEN) {
97                         fr_strerror_printf("Discarding packet: Smaller than RFC minimum of 20 bytes");
98                         return -1;
99                 }
100
101                 /*
102                  *      If the packet is too big, then the socket is bad.
103                  */
104                 if (packet_len > MAX_PACKET_LEN) {
105                         fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
106                         return -1;
107                 }
108
109                 packet->data = talloc_array(packet, uint8_t, packet_len);
110                 if (!packet->data) {
111                         fr_strerror_printf("Out of memory");
112                         return -1;
113                 }
114
115                 packet->data_len = packet_len;
116                 packet->partial = 4;
117                 memcpy(packet->data, packet->vector, 4);
118         }
119
120         /*
121          *      Try to read more data.
122          */
123         len = recv(packet->sockfd, packet->data + packet->partial,
124                    packet->data_len - packet->partial, 0);
125         if (len == 0) return -2; /* clean close */
126
127 #ifdef ECONNRESET
128         if ((len < 0) && (errno == ECONNRESET)) { /* forced */
129                 return -2;
130         }
131 #endif
132
133         if (len < 0) {
134                 fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
135                 return -1;
136         }
137
138         packet->partial += len;
139
140         if (packet->partial < packet->data_len) {
141                 return 0;
142         }
143
144         /*
145          *      See if it's a well-formed RADIUS packet.
146          */
147         if (!rad_packet_ok(packet, flags, NULL)) {
148                 return -1;
149         }
150
151         /*
152          *      Explicitly set the VP list to empty.
153          */
154         packet->vps = NULL;
155
156         if (fr_debug_lvl) {
157                 char ip_buf[128], buffer[256];
158
159                 if (packet->src_ipaddr.af != AF_UNSPEC) {
160                         inet_ntop(packet->src_ipaddr.af,
161                                   &packet->src_ipaddr.ipaddr,
162                                   ip_buf, sizeof(ip_buf));
163                         snprintf(buffer, sizeof(buffer), "host %s port %d",
164                                  ip_buf, packet->src_port);
165                 } else {
166                         snprintf(buffer, sizeof(buffer), "socket %d",
167                                  packet->sockfd);
168                 }
169
170
171                 if (is_radius_code(packet->code)) {
172                         DEBUG("Received %s packet from %s",
173                               fr_packet_codes[packet->code], buffer);
174                 } else {
175                         DEBUG("Received packet from %s code %d",
176                               buffer, packet->code);
177                 }
178                 DEBUG(", id=%d, length=%zu\n", packet->id, packet->data_len);
179         }
180
181         return 1;               /* done reading the packet */
182 }
183
184 #endif /* WITH_TCP */