/src/freeradius-server/src/lib/util/udp.c
Line | Count | Source |
1 | | /* |
2 | | * This library is free software; you can redistribute it and/or |
3 | | * modify it under the terms of the GNU Lesser General Public |
4 | | * License as published by the Free Software Foundation; either |
5 | | * version 2.1 of the License, or (at your option) any later version. |
6 | | * |
7 | | * This library 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 GNU |
10 | | * Lesser General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU Lesser General Public |
13 | | * License along with this library; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** Utility functions for managing UDP sockets |
18 | | * |
19 | | * @file src/lib/util/udp.c |
20 | | * |
21 | | * @copyright 2000-2003,2006 The FreeRADIUS server project |
22 | | */ |
23 | | RCSID("$Id: afda7588b05cb2d3e8285b43aab85f044dbccf7d $") |
24 | | |
25 | | #include <freeradius-devel/util/log.h> |
26 | | #include <freeradius-devel/util/socket.h> |
27 | | #include <freeradius-devel/util/syserror.h> |
28 | | #include <freeradius-devel/util/udp.h> |
29 | | |
30 | 0 | #define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf |
31 | | |
32 | | /** Send a packet via a UDP socket. |
33 | | * |
34 | | * @param[in] sock we're reading from. |
35 | | * @param[in] flags to pass to send(), or sendto() |
36 | | * @param[in] data to data to send |
37 | | * @param[in] data_len length of data to send |
38 | | * @return |
39 | | * - >=0 amount of data written. |
40 | | * - -1 on failure. |
41 | | */ |
42 | | int udp_send(fr_socket_t const *sock, int flags, void *data, size_t data_len) |
43 | 0 | { |
44 | 0 | int ret; |
45 | |
|
46 | 0 | fr_assert(sock->type == SOCK_DGRAM); |
47 | |
|
48 | 0 | if (flags & UDP_FLAGS_CONNECTED) { |
49 | 0 | ret = send(sock->fd, data, data_len, 0); |
50 | 0 | } else { |
51 | 0 | struct sockaddr_storage dst, src; |
52 | 0 | socklen_t sizeof_dst, sizeof_src; |
53 | |
|
54 | 0 | if (fr_ipaddr_to_sockaddr(&dst, &sizeof_dst, |
55 | 0 | &sock->inet.dst_ipaddr, sock->inet.dst_port) < 0) return -1; |
56 | 0 | if (fr_ipaddr_to_sockaddr(&src, &sizeof_src, |
57 | 0 | &sock->inet.src_ipaddr, sock->inet.src_port) < 0) return -1; |
58 | | |
59 | 0 | ret = sendfromto(sock->fd, data, data_len, 0, |
60 | 0 | sock->inet.ifindex, |
61 | 0 | (struct sockaddr *)&src, sizeof_src, |
62 | 0 | (struct sockaddr *)&dst, sizeof_dst); |
63 | 0 | } |
64 | | |
65 | 0 | if (ret < 0) fr_strerror_printf("udp_send failed: %s", fr_syserror(errno)); |
66 | |
|
67 | 0 | return ret; |
68 | 0 | } |
69 | | |
70 | | |
71 | | /** Discard the next UDP packet |
72 | | * |
73 | | * @param[in] sockfd we're reading from. |
74 | | */ |
75 | | int udp_recv_discard(int sockfd) |
76 | 0 | { |
77 | 0 | uint8_t data[4]; |
78 | 0 | struct sockaddr_storage src; |
79 | 0 | socklen_t sizeof_src = sizeof(src); |
80 | |
|
81 | 0 | return recvfrom(sockfd, data, sizeof(data), 0, |
82 | 0 | (struct sockaddr *)&src, &sizeof_src); |
83 | 0 | } |
84 | | |
85 | | |
86 | | /** Peek at the header of a UDP packet. |
87 | | * |
88 | | * @param[in] sockfd we're reading from. |
89 | | * @param[out] data pointer where data will be written |
90 | | * @param[in] data_len length of data to read |
91 | | * @param[in] flags for things |
92 | | * @param[out] src_ipaddr of the packet. |
93 | | * @param[out] src_port of the packet. |
94 | | */ |
95 | | ssize_t udp_recv_peek(int sockfd, void *data, size_t data_len, int flags, fr_ipaddr_t *src_ipaddr, uint16_t *src_port) |
96 | 0 | { |
97 | 0 | ssize_t peeked; |
98 | 0 | struct sockaddr_storage src; |
99 | 0 | socklen_t sizeof_src = sizeof(src); |
100 | |
|
101 | 0 | if (!src_ipaddr || ((flags & UDP_FLAGS_CONNECTED) != 0)) { |
102 | 0 | peeked = recv(sockfd, data, data_len, MSG_PEEK); |
103 | 0 | if (peeked < 0) { |
104 | 0 | if ((errno == EAGAIN) || (errno == EINTR)) return 0; |
105 | 0 | return -1; |
106 | 0 | } |
107 | | |
108 | 0 | return peeked; |
109 | 0 | } |
110 | | |
111 | 0 | peeked = recvfrom(sockfd, data, data_len, MSG_PEEK, (struct sockaddr *)&src, &sizeof_src); |
112 | 0 | if (peeked < 0) { |
113 | 0 | if ((errno == EAGAIN) || (errno == EINTR)) return 0; |
114 | 0 | return -1; |
115 | 0 | } |
116 | | |
117 | | /* |
118 | | * Convert AF. If unknown, discard packet. |
119 | | */ |
120 | 0 | if (fr_ipaddr_from_sockaddr(src_ipaddr, src_port, &src, sizeof_src) < 0) { |
121 | 0 | FR_DEBUG_STRERROR_PRINTF("Unknown address family"); |
122 | 0 | (void) udp_recv_discard(sockfd); |
123 | |
|
124 | 0 | return -1; |
125 | 0 | } |
126 | | |
127 | 0 | return peeked; |
128 | 0 | } |
129 | | |
130 | | |
131 | | /** Read a UDP packet |
132 | | * |
133 | | * @param[in] sockfd we're reading from. |
134 | | * @param[in] flags for things |
135 | | * @param[out] socket_out Information about the src/dst address of the packet |
136 | | * and the interface it was received on. |
137 | | * @param[out] data pointer where data will be written |
138 | | * @param[in] data_len length of data to read |
139 | | * @param[out] when the packet was received. |
140 | | * @return |
141 | | * - > 0 on success (number of bytes read). |
142 | | * - < 0 on failure. |
143 | | */ |
144 | | ssize_t udp_recv(int sockfd, int flags, |
145 | | fr_socket_t *socket_out, void *data, size_t data_len, fr_time_t *when) |
146 | 0 | { |
147 | 0 | int sock_flags = 0; |
148 | 0 | struct sockaddr_storage src; |
149 | 0 | struct sockaddr_storage dst; |
150 | 0 | socklen_t sizeof_src = sizeof(src); |
151 | 0 | socklen_t sizeof_dst = sizeof(dst); |
152 | 0 | ssize_t slen; |
153 | |
|
154 | 0 | if ((flags & UDP_FLAGS_PEEK) != 0) sock_flags |= MSG_PEEK; |
155 | |
|
156 | 0 | if (when) *when = fr_time_wrap(0); |
157 | | |
158 | | /* |
159 | | * Always initialise the output socket structure |
160 | | */ |
161 | 0 | *socket_out = (fr_socket_t){ |
162 | 0 | .fd = sockfd, |
163 | 0 | .type = SOCK_DGRAM, |
164 | 0 | }; |
165 | | |
166 | | /* |
167 | | * Connected sockets already know src/dst IP/port |
168 | | */ |
169 | 0 | if ((flags & UDP_FLAGS_CONNECTED) != 0) { |
170 | 0 | slen = recv(sockfd, data, data_len, sock_flags); |
171 | 0 | goto done; |
172 | 0 | } |
173 | | |
174 | | /* |
175 | | * Receive the packet. The OS will discard any data in the |
176 | | * packet after "len" bytes. |
177 | | */ |
178 | 0 | slen = recvfromto(sockfd, data, data_len, sock_flags, |
179 | 0 | &socket_out->inet.ifindex, |
180 | 0 | (struct sockaddr *)&src, &sizeof_src, |
181 | 0 | (struct sockaddr *)&dst, &sizeof_dst, |
182 | 0 | when); |
183 | 0 | if (slen <= 0) goto done; |
184 | | |
185 | 0 | if (fr_ipaddr_from_sockaddr(&socket_out->inet.src_ipaddr, &socket_out->inet.src_port, &src, sizeof_src) < 0) { |
186 | 0 | fr_strerror_const_push("Failed converting src sockaddr to ipaddr"); |
187 | 0 | return -1; |
188 | 0 | } |
189 | 0 | if (fr_ipaddr_from_sockaddr(&socket_out->inet.dst_ipaddr, &socket_out->inet.dst_port, &dst, sizeof_dst) < 0) { |
190 | 0 | fr_strerror_const_push("Failed converting dst sockaddr to ipaddr"); |
191 | 0 | return -1; |
192 | 0 | } |
193 | | |
194 | 0 | done: |
195 | 0 | if (slen < 0) { |
196 | 0 | if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) return 0; |
197 | | |
198 | 0 | fr_strerror_printf("Failed reading socket: %s", fr_syserror(errno)); |
199 | 0 | return slen; |
200 | 0 | } |
201 | | |
202 | | /* |
203 | | * We didn't get it from the kernel |
204 | | * so use our own time source. |
205 | | */ |
206 | 0 | if (when && fr_time_eq(*when, fr_time_wrap(0))) *when = fr_time(); |
207 | |
|
208 | 0 | return slen; |
209 | 0 | } |