* Copyright (C) 2009 Dante http://dante.net
*/
-#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/libradius.h>
-#include <freeradius-devel/tcp.h>
#ifdef WITH_TCP
#define MAX_PACKET_LEN 4096
/*
- * Open a socket on the given IP and port.
- */
-int fr_tcp_socket(fr_ipaddr_t *ipaddr, int port)
-{
- int sockfd;
- int on = 1;
- struct sockaddr_storage salocal;
- socklen_t salen;
-
- if ((port < 0) || (port > 65535)) {
- fr_strerror_printf("Port %d is out of allowed bounds", port);
- return -1;
- }
-
- sockfd = socket(ipaddr->af, SOCK_STREAM, 0);
- if (sockfd < 0) {
- return sockfd;
- }
-
- if (fr_nonblock(sockfd) < 0) {
- close(sockfd);
- return -1;
- }
-
- if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
- close(sockfd);
- return -1;
- }
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
- if (ipaddr->af == AF_INET6) {
- /*
- * Listening on '::' does NOT get you IPv4 to
- * IPv6 mapping. You've got to listen on an IPv4
- * address, too. This makes the rest of the server
- * design a little simpler.
- */
-#ifdef IPV6_V6ONLY
-
- if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
- setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
- (char *)&on, sizeof(on));
- }
-#endif /* IPV6_V6ONLY */
- }
-#endif /* HAVE_STRUCT_SOCKADDR_IN6 */
-
- if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
- fr_strerror_printf("Failed in setsockopt(): %s", strerror(errno));
- close(sockfd);
- return -1;
- }
-
- if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
- fr_strerror_printf("Failed in bind(): %s", strerror(errno));
- close(sockfd);
- return -1;
- }
-
- if (listen(sockfd, 8) < 0) {
- fr_strerror_printf("Failed in listen(): %s", strerror(errno));
- close(sockfd);
- return -1;
- }
-
- return sockfd;
-}
-
-
-/*
* Open a socket TO the given IP and port.
*/
int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
- fr_ipaddr_t *dst_ipaddr, int dst_port)
+ fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
{
int sockfd;
struct sockaddr_storage salocal;
socklen_t salen;
- if ((dst_port < 0) || (dst_port > 65535)) {
- fr_strerror_printf("Port %d is out of allowed bounds",
- dst_port);
- return -1;
- }
-
if (!dst_ipaddr) return -1;
sockfd = socket(dst_ipaddr->af, SOCK_STREAM, 0);
return sockfd;
}
-#if 0
-#ifdef O_NONBLOCK
- {
- int flags;
-
- if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0) {
- fr_strerror_printf("Failure getting socket flags: %s",
- strerror(errno));
- close(sockfd);
- return -1;
- }
-
- flags |= O_NONBLOCK;
- if( fcntl(sockfd, F_SETFL, flags) < 0) {
- fr_strerror_printf("Failure setting socket flags: %s",
- strerror(errno));
- close(sockfd);
- return -1;
- }
- }
-#endif
-#endif
/*
* Allow the caller to bind us to a specific source IP.
*/
close(sockfd);
return -1;
}
-
+
if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
fr_strerror_printf("Failure binding to IP: %s",
- strerror(errno));
+ fr_syserror(errno));
close(sockfd);
return -1;
}
}
if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
- close(sockfd);
+ close(sockfd);
return -1;
}
* socket is ready...
*/
if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
- fr_strerror_printf("Failed in connect(): %s", strerror(errno));
+ fr_strerror_printf("Failed in connect(): %s", fr_syserror(errno));
close(sockfd);
return -1;
}
RADIUS_PACKET *fr_tcp_recv(int sockfd, int flags)
{
- RADIUS_PACKET *packet = rad_alloc(0);
+ RADIUS_PACKET *packet = rad_alloc(NULL, false);
if (!packet) return NULL;
len = recv(packet->sockfd, packet->vector + packet->data_len,
4 - packet->data_len, 0);
- if ((len == 0) || /* clean close */
- ((len < 0) && (errno == ECONNRESET))) { /* forced */
+ if (len == 0) return -2; /* clean close */
+
+#ifdef ECONNRESET
+ if ((len < 0) && (errno == ECONNRESET)) { /* forced */
return -2;
}
+#endif
if (len < 0) {
fr_strerror_printf("Error receiving packet: %s",
- strerror(errno));
+ fr_syserror(errno));
return -1;
}
packet_len = (packet->vector[2] << 8) | packet->vector[3];
if (packet_len < AUTH_HDR_LEN) {
- fr_strerror_printf("Discarding packet: Smaller than RFC minimum of 20 bytes.");
+ fr_strerror_printf("Discarding packet: Smaller than RFC minimum of 20 bytes");
return -1;
}
* If the packet is too big, then the socket is bad.
*/
if (packet_len > MAX_PACKET_LEN) {
- fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
+ fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
return -1;
}
-
- packet->data = malloc(packet_len);
+
+ packet->data = talloc_array(packet, uint8_t, packet_len);
if (!packet->data) {
fr_strerror_printf("Out of memory");
return -1;
*/
len = recv(packet->sockfd, packet->data + packet->partial,
packet->data_len - packet->partial, 0);
- if ((len == 0) || /* clean close */
- ((len < 0) && (errno == ECONNRESET))) { /* forced */
- return -2;
+ if (len == 0) return -2; /* clean close */
+
+#ifdef ECONNRESET
+ if ((len < 0) && (errno == ECONNRESET)) { /* forced */
+ return -2;
}
+#endif
if (len < 0) {
- fr_strerror_printf("Error receiving packet: %s", strerror(errno));
+ fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
return -1;
}
/*
* See if it's a well-formed RADIUS packet.
*/
- if (!rad_packet_ok(packet, flags)) {
+ if (!rad_packet_ok(packet, flags, NULL)) {
return -1;
}
}
- if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
- DEBUG("rad_recv: %s packet from %s",
+ if (is_radius_code(packet->code)) {
+ DEBUG("Received %s packet from %s",
fr_packet_codes[packet->code], buffer);
} else {
- DEBUG("rad_recv: Packet from %s code=%d",
+ DEBUG("Received packet from %s code %d",
buffer, packet->code);
}
- DEBUG(", id=%d, length=%ld\n", packet->id, packet->data_len);
+ DEBUG(", id=%d, length=%zu\n", packet->id, packet->data_len);
}
return 1; /* done reading the packet */
}
-RADIUS_PACKET *fr_tcp_accept(int sockfd)
-{
- int newfd;
- socklen_t salen;
- RADIUS_PACKET *packet;
- struct sockaddr_storage src;
-
- salen = sizeof(src);
-
- newfd = accept(sockfd, (struct sockaddr *) &src, &salen);
- if (newfd < 0) {
- /*
- * Non-blocking sockets must handle this.
- */
- if (errno == EWOULDBLOCK) {
- packet = rad_alloc(0);
- if (!packet) return NULL;
-
- packet->sockfd = sockfd;
- packet->src_ipaddr.af = AF_UNSPEC;
- return packet;
- }
- }
-
- packet = rad_alloc(0);
- if (!packet) return NULL;
-
- if (src.ss_family == AF_INET) {
- struct sockaddr_in s4;
-
- memcpy(&s4, &src, sizeof(s4));
- packet->src_ipaddr.af = AF_INET;
- packet->src_ipaddr.ipaddr.ip4addr = s4.sin_addr;
- packet->src_port = ntohs(s4.sin_port);
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
- } else if (src.ss_family == AF_INET6) {
- struct sockaddr_in6 s6;
-
- memcpy(&s6, &src, sizeof(s6));
- packet->src_ipaddr.af = AF_INET6;
- packet->src_ipaddr.ipaddr.ip6addr = s6.sin6_addr;
- packet->src_port = ntohs(s6.sin6_port);
-
-#endif
- } else {
- rad_free(&packet);
- return NULL;
- }
-
- packet->sockfd = newfd;
-
- /*
- * Note: Caller has to set dst_ipaddr && dst_port.
- */
- return packet;
-}
-
-
-/*
- * Writes a packet, assuming it's already been encoded.
- *
- * It returns the number of bytes written, which MAY be less than
- * the packet size (data_len). It is the caller's responsibility
- * to check the return code, and to schedule writes again.
- */
-ssize_t fr_tcp_write_packet(RADIUS_PACKET *packet)
-{
- ssize_t rcode;
-
- if (!packet || !packet->data) return 0;
-
- if (packet->partial >= packet->data_len) return packet->data_len;
-
- rcode = write(packet->sockfd, packet->data + packet->partial,
- packet->data_len - packet->partial);
- if (rcode < 0) return packet->partial; /* ignore most errors */
-
- packet->partial += rcode;
-
- return packet->partial;
-}
#endif /* WITH_TCP */