fr_event_list_t *el; //!< Event list.
trunk_t *trunk; //!< trunk handler
fr_bio_fd_config_t *fd_config; //!< for threads or sockets
+ fr_bio_fd_info_t const *fd_info; //!< status of the FD.
fr_radius_ctx_t radius_ctx;
} bio_handle_ctx_t;
struct {
fr_bio_t *fd; //!< writing
- fr_bio_fd_info_t const *info; //!< for replication
uint32_t id; //!< for replication
fr_rb_expire_t expires; //!< for proxying / client sending
} bio;
fr_bio_t *fd; //!< raw FD
fr_bio_t *mem; //!< memory wrappers for stream sockets
} bio;
- fr_bio_fd_info_t const *fd_info;
connection_t *conn;
h = talloc_get_type_abort(conn->h, bio_handle_t);
- ERROR("%s - Connection %s failed: %s", h->ctx.module_name, h->fd_info->name, fr_syserror(fd_errno));
+ ERROR("%s - Connection %s failed: %s", h->ctx.module_name, h->ctx.fd_info->name, fr_syserror(fd_errno));
connection_signal_reconnect(conn, CONNECTION_FAILED);
}
fr_pair_list_init(&reply);
slen = fr_bio_read(h->bio.read, NULL, h->buffer, h->buflen);
- if (slen == 0) return;
+ if (slen == 0) {
+ /*
+ * @todo - set BIO FD EOF callback, so that we don't have to check it here.
+ */
+ if (h->ctx.fd_info->eof) goto failed;
+ return;
+ }
+
+ /*
+ * We're done reading, return.
+ */
+ if (slen == fr_bio_error(IO_WOULD_BLOCK)) return;
if (slen < 0) {
switch (errno) {
-#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
- case EWOULDBLOCK:
-#endif
- case EAGAIN:
- case EINTR:
- return; /* Wait to be signalled again */
-
case ECONNREFUSED:
ERROR("%s - Failed reading response from socket: there is no server listening on outgoing connection %s",
- h->ctx.module_name, h->fd_info->name);
+ h->ctx.module_name, h->ctx.fd_info->name);
break;
default:
break;
}
+ failed:
connection_signal_reconnect(conn, CONNECTION_FAILED);
return;
}
* the next status check.
*/
DEBUG("%s - Received %u / %u replies for status check, on connection - %s",
- h->ctx.module_name, u->num_replies, inst->num_answers_to_alive, h->fd_info->name);
+ h->ctx.module_name, u->num_replies, inst->num_answers_to_alive, h->ctx.fd_info->name);
DEBUG("%s - Next status check packet will be in %pVs",
h->ctx.module_name, fr_box_time_delta(fr_time_sub(u->retry.next, fr_time())));
*/
status_check_reset(h, u);
- DEBUG("%s - Connection open - %s", h->ctx.module_name, h->fd_info->name);
+ DEBUG("%s - Connection open - %s", h->ctx.module_name, h->ctx.fd_info->name);
connection_signal_connected(conn);
}
}
DEBUG("%s - Sending %s ID %d over connection %s",
- h->ctx.module_name, fr_radius_packet_name[u->code], u->id, h->fd_info->name);
+ h->ctx.module_name, fr_radius_packet_name[u->code], u->id, h->ctx.fd_info->name);
if (encode(h, h->status_request, u, u->id) < 0) {
fail:
HEXDUMP3(u->packet, u->packet_len, NULL);
slen = fr_bio_write(h->bio.write, NULL, u->packet, u->packet_len);
+
+ if (slen == fr_bio_error(IO_WOULD_BLOCK)) goto blocked;
+
if (slen < 0) {
ERROR("%s - Failed sending %s ID %d length %zu over connection %s: %s",
- h->ctx.module_name, fr_radius_packet_name[u->code], u->id, u->packet_len, h->fd_info->name, fr_syserror(errno));
+ h->ctx.module_name, fr_radius_packet_name[u->code], u->id, u->packet_len, h->ctx.fd_info->name, fr_syserror(errno));
goto fail;
}
/*
- * @todo - write partial packets, too. <sigh>
+ * @todo - handle partial packets and blocked writes.
*/
if ((size_t)slen < u->packet_len) {
+ blocked:
ERROR("%s - Failed sending %s ID %d length %zu over connection %s: writing is blocked",
- h->ctx.module_name, fr_radius_packet_name[u->code], u->id, u->packet_len, h->fd_info->name);
+ h->ctx.module_name, fr_radius_packet_name[u->code], u->id, u->packet_len, h->ctx.fd_info->name);
goto fail;
}
* The connection code will take care of deleting the FD from the event loop.
*/
- DEBUG("%s - Connection closed - %s", h->ctx.module_name, h->fd_info->name);
+ DEBUG("%s - Connection closed - %s", h->ctx.module_name, h->ctx.fd_info->name);
return 0;
}
{
bio_handle_t *h = bio->uctx;
- DEBUG("%s - Connection open - %s", h->ctx.module_name, h->fd_info->name);
+ DEBUG("%s - Connection open - %s", h->ctx.module_name, h->ctx.fd_info->name);
connection_signal_connected(h->conn);
}
{
bio_handle_t *h = bio->uctx;
- DEBUG("%s - Connection failed - %s - %s", h->ctx.module_name, h->fd_info->name,
- fr_syserror(h->fd_info->connect_errno));
+ DEBUG("%s - Connection failed - %s - %s", h->ctx.module_name, h->ctx.fd_info->name,
+ fr_syserror(h->ctx.fd_info->connect_errno));
connection_signal_reconnect(h->conn, CONNECTION_FAILED);
}
*/
want = fr_nbo_to_uint16(hdr + 2);
if (want > h->ctx.inst->max_packet_size) {
- ERROR("%s - Connection %s received too long packet", h->ctx.module_name, h->fd_info->name);
+ ERROR("%s - Connection %s received too long packet", h->ctx.module_name, h->ctx.fd_info->name);
return FR_BIO_VERIFY_ERROR_CLOSE;
}
if (!fr_radius_ok(data, size, h->ctx.inst->max_attributes, REQUIRE_MA(h), &failure)) {
if (failure == DECODE_FAIL_UNKNOWN_PACKET_CODE) return FR_BIO_VERIFY_DISCARD;
- PERROR("%s - Connection %s received bad packet", h->ctx.module_name, h->fd_info->name);
+ PERROR("%s - Connection %s received bad packet", h->ctx.module_name, h->ctx.fd_info->name);
return FR_BIO_VERIFY_ERROR_CLOSE;
}
}
h->bio.fd->uctx = h;
- h->fd_info = fr_bio_fd_info(h->bio.fd);
+ h->ctx.fd_info = fr_bio_fd_info(h->bio.fd);
- fd = h->fd_info->socket.fd;
+ fd = h->ctx.fd_info->socket.fd;
fr_assert(fd >= 0);
/*
/*
* If the socket isn't connected, then do that first.
*/
- if (h->fd_info->state != FR_BIO_FD_STATE_OPEN) {
+ if (h->ctx.fd_info->state != FR_BIO_FD_STATE_OPEN) {
int rcode;
- fr_assert(h->fd_info->state == FR_BIO_FD_STATE_CONNECTING);
+ fr_assert(h->ctx.fd_info->state == FR_BIO_FD_STATE_CONNECTING);
/*
- * @todo - call connect_full() with callbacks, timeouts, etc.
+ * We don't pass timeouts here because the trunk has it's own connection timeouts.
*/
rcode = fr_bio_fd_connect_full(h->bio.fd, conn->el, bio_connected, bio_error, NULL, NULL);
if (rcode < 0) goto fail;
connection_t *conn = tconn->conn;
bio_handle_t *h = talloc_get_type_abort(conn->h, bio_handle_t);
- ERROR("%s - Connection %s failed: %s", h->ctx.module_name, h->fd_info->name, fr_syserror(fd_errno));
+ ERROR("%s - Connection %s failed: %s", h->ctx.module_name, h->ctx.fd_info->name, fr_syserror(fd_errno));
connection_signal_reconnect(conn, CONNECTION_FAILED);
}
bio_request_t const *b = two;
int8_t ret;
- // @todo - prioritize packets if there's a state?
-
/*
* Prioritise status check packets
*/
code = data[0];
RDEBUG("Received %s ID %d length %zu reply packet on connection %s",
- fr_radius_packet_name[code], data[1], data_len, h->fd_info->name);
+ fr_radius_packet_name[code], data[1], data_len, h->ctx.fd_info->name);
log_request_pair_list(L_DBG_LVL_2, request, NULL, reply, NULL);
/*
trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
bio_handle_t *h = talloc_get_type_abort(tconn->conn->h, bio_handle_t);
- INFO("%s - Reviving connection %s", h->ctx.module_name, h->fd_info->name);
+ INFO("%s - Reviving connection %s", h->ctx.module_name, h->ctx.fd_info->name);
trunk_connection_signal_reconnect(tconn, CONNECTION_FAILED);
}
trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
bio_handle_t *h = talloc_get_type_abort(tconn->conn->h, bio_handle_t);
- INFO("%s - No replies during 'zombie_period', marking connection %s as dead", h->ctx.module_name, h->fd_info->name);
+ INFO("%s - No replies during 'zombie_period', marking connection %s as dead", h->ctx.module_name, h->ctx.fd_info->name);
/*
* Don't use this connection, and re-queue all of its
/*
* Stop using it for new requests.
*/
- WARN("%s - Entering Zombie state - connection %s", h->ctx.module_name, h->fd_info->name);
+ WARN("%s - Entering Zombie state - connection %s", h->ctx.module_name, h->ctx.fd_info->name);
trunk_connection_signal_inactive(tconn);
if (h->ctx.inst->status_check) {
/*
* Arguably this should never happen for UDP sockets.
*/
- if (h->fd_info->write_blocked) {
+ if (h->ctx.fd_info->write_blocked) {
RDEBUG("IO is blocked - suppressing retransmission");
return;
}
u->id = u->rr->id;
RDEBUG("Sending %s ID %d length %zu over connection %s",
- fr_radius_packet_name[u->code], u->id, u->packet_len, h->fd_info->name);
+ fr_radius_packet_name[u->code], u->id, u->packet_len, h->ctx.fd_info->name);
if (encode(h, request, u, u->id) < 0) {
/*
(void) radius_track_entry_update(u->rr, u->packet + RADIUS_AUTH_VECTOR_OFFSET);
} else {
RDEBUG("Retransmitting %s ID %d length %zu over connection %s",
- fr_radius_packet_name[u->code], u->id, u->packet_len, h->fd_info->name);
+ fr_radius_packet_name[u->code], u->id, u->packet_len, h->ctx.fd_info->name);
}
/*
do_write:
slen = fr_bio_write(h->bio.write, NULL, packet, packet_len);
- if (slen < 0) {
- /*
- * @todo - check slen for fr_bio_error(FOO)
- */
- /*
- * Temporary conditions
- */
- switch (errno) {
- /*
- * The BIO code should catch EAGAIN, EWOULDBLOCK, EINTR,
- * and return "0 bytes written".
- */
-#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
- case EWOULDBLOCK: /* No outbound packet buffers, maybe? */
-#endif
- case EAGAIN: /* No outbound packet buffers, maybe? */
- case EINTR: /* Interrupted by signal */
-
- case ENOBUFS: /* No outbound packet buffers, maybe? */
- case ENOMEM: /* malloc failure in kernel? */
- RWARN("%s - Failed sending data over connection %s: %s",
- h->ctx.module_name, h->fd_info->name, fr_syserror(errno));
- trunk_request_signal_fail(treq);
- break;
+ /*
+ * Can't write anything, requeue it on a different socket.
+ */
+ if (slen == fr_bio_error(IO_WOULD_BLOCK)) goto requeue;
+ if (slen < 0) {
+ switch (errno) {
/*
- * Fatal, request specific conditions
+ * There is an error in the request.
*/
case EMSGSIZE: /* Packet size exceeds max size allowed on socket */
ERROR("%s - Failed sending data over connection %s: %s",
- h->ctx.module_name, h->fd_info->name, fr_syserror(errno));
+ h->ctx.module_name, h->ctx.fd_info->name, fr_syserror(errno));
trunk_request_signal_fail(treq);
break;
/*
- * Will re-queue any 'sent' requests, so we don't
- * have to do any cleanup.
+ * There is an error in the connection. The reconnection will re-queue any pending or
+ * sent requests, so we don't have to do any cleanup.
*/
default:
ERROR("%s - Failed sending data over connection %s: %s",
- h->ctx.module_name, h->fd_info->name, fr_syserror(errno));
+ h->ctx.module_name, h->ctx.fd_info->name, fr_syserror(errno));
trunk_connection_signal_reconnect(treq->tconn, CONNECTION_FAILED);
break;
}
if (slen == 0) {
if (u->partial) return;
+ requeue:
RWARN("%s - Failed sending data over connection %s: sent zero bytes",
- h->ctx.module_name, h->fd_info->name);
+ h->ctx.module_name, h->ctx.fd_info->name);
trunk_request_requeue(treq);
return;
}
if (response_length < 4096) response_length = 4096;
if (response_length > 65535) response_length = 65535;
- DEBUG("%s - Increasing buffer size to %u for connection %s", h->ctx.module_name, response_length, h->fd_info->name);
+ DEBUG("%s - Increasing buffer size to %u for connection %s", h->ctx.module_name, response_length, h->ctx.fd_info->name);
/*
* Make sure to copy the packet over!
if (u->num_replies < inst->num_answers_to_alive) {
DEBUG("Received %u / %u replies for status check, on connection - %s",
- u->num_replies, inst->num_answers_to_alive, h->fd_info->name);
+ u->num_replies, inst->num_answers_to_alive, h->ctx.fd_info->name);
DEBUG("Next status check packet will be in %pVs", fr_box_time_delta(fr_time_sub(u->retry.next, now)));
/*
return;
}
- DEBUG("Received enough replies to status check, marking connection as active - %s", h->fd_info->name);
+ DEBUG("Received enough replies to status check, marking connection as active - %s", h->ctx.fd_info->name);
/*
* Set the "last idle" time to now, so that we don't
{
bio_handle_t *h = talloc_get_type_abort(conn->h, bio_handle_t);
- DEBUG3("%s - Reading data for connection %s", h->ctx.module_name, h->fd_info->name);
+ DEBUG3("%s - Reading data for connection %s", h->ctx.module_name, h->ctx.fd_info->name);
while (true) {
ssize_t slen;
* busy, a few extra system calls don't matter.
*/
slen = fr_bio_read(h->bio.read, NULL, h->buffer, h->buflen);
- if (slen == 0) return;
+ if (slen == 0) {
+ /*
+ * @todo - set BIO FD EOF callback, so that we don't have to check it here.
+ */
+ if (h->ctx.fd_info->eof) trunk_connection_signal_reconnect(tconn, CONNECTION_FAILED);
+ return;
+ }
- if (slen < 0) {
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) return;
+ /*
+ * We're done reading, return.
+ */
+ if (slen == fr_bio_error(IO_WOULD_BLOCK)) return;
+ if (slen < 0) {
ERROR("%s - Failed reading response from socket: %s",
h->ctx.module_name, fr_syserror(errno));
trunk_connection_signal_reconnect(tconn, CONNECTION_FAILED);
}
thread->bio.fd->uctx = thread;
- thread->bio.info = fr_bio_fd_info(thread->bio.fd);
+ thread->ctx.fd_info = fr_bio_fd_info(thread->bio.fd);
/*
* We don't care about replies.
if (inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) {
(void) fr_bio_fd_write_only(thread->bio.fd);
- DEBUG("%s - Opened unconnected replication socket %s", inst->name, thread->bio.info->name);
+ DEBUG("%s - Opened unconnected replication socket %s", inst->name, thread->ctx.fd_info->name);
return 0;
}
- DEBUG("%s - Opened unconnected proxy socket %s", inst->name, thread->bio.info->name);
+ DEBUG("%s - Opened unconnected proxy socket %s", inst->name, thread->ctx.fd_info->name);
/*
* @todo - make lifetime configurable?
/*
* Can't change IP address families.
*/
- if (ipaddr->vb_ip.af != thread->bio.info->socket.af) {
+ if (ipaddr->vb_ip.af != thread->ctx.fd_info->socket.af) {
RDEBUG("Invalid destination IP address family in %pV", ipaddr);
return -1;
}
* Prepare destination address.
*/
addr = (fr_bio_fd_packet_ctx_t) {
- .socket = thread->bio.info->socket,
+ .socket = thread->ctx.fd_info->socket,
};
addr.socket.inet.dst_ipaddr = ipaddr->vb_ip;
addr.socket.inet.dst_port = port->vb_uint16;
/*
* Can't change IP address families.
*/
- if (ipaddr->vb_ip.af != thread->bio.info->socket.af) {
+ if (ipaddr->vb_ip.af != thread->ctx.fd_info->socket.af) {
RDEBUG("Invalid destination IP address family in %pV", ipaddr);
return XLAT_ACTION_DONE;
}