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