Coverage Report

Created: 2026-05-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}