/** Handle a network control message callback for a new "watch directory" * * @param[in] ctx the network * @param[in] data the message * @param[in] data_size size of the data * @param[in] now the current time */ static void fr_network_directory_callback(void *ctx, void const *data, size_t data_size, UNUSED fr_time_t now) { int num_messages; fr_network_t *nr = ctx; fr_network_socket_t *s; fr_app_io_t const *app_io; fr_event_vnode_func_t funcs = { .extend = fr_network_vnode_extend }; rad_assert(data_size == sizeof(s->listen)); if (data_size != sizeof(s->listen)) return; s = talloc_zero(nr, fr_network_socket_t); rad_assert(s != NULL); s->nr = nr; memcpy(&s->listen, data, sizeof(s->listen)); s->number = nr->num_sockets++; MEM(s->waiting = fr_heap_create(s, waiting_cmp, fr_channel_data_t, channel.heap_id)); talloc_set_destructor(s, _network_socket_free); /* * Allocate the ring buffer for messages and packets. */ num_messages = s->listen->num_messages; if (num_messages < 8) num_messages = 8; s->ms = fr_message_set_create(s, num_messages, sizeof(fr_channel_data_t), s->listen->default_message_size * s->listen->num_messages); if (!s->ms) { fr_log(nr->log, L_ERR, "Failed creating message buffers for directory IO: %s", fr_strerror()); talloc_free(s); return; } app_io = s->listen->app_io; if (app_io->event_list_set) app_io->event_list_set(s->listen->app_io_instance, nr->el, nr); rad_assert(app_io->fd); s->fd = app_io->fd(s->listen->app_io_instance); s->filter = FR_EVENT_FILTER_VNODE; if (fr_event_filter_insert(nr, nr->el, s->fd, s->filter, &funcs, app_io->error ? fr_network_error : NULL, s) < 0) { PERROR("Failed adding new socket to event loop"); talloc_free(s); return; } (void) rbtree_insert(nr->sockets, s); (void) rbtree_insert(nr->sockets_by_num, s); DEBUG3("Using new socket with FD %d", s->fd); }
void log_hex(fr_log_t const *log, fr_log_type_t type, fr_log_lvl_t lvl, uint8_t const *data, size_t data_len) { size_t i, j, len; char *p; char buffer[(0x10 * 3) + 1]; if (!debug_enabled(L_DBG, lvl)) return; for (i = 0; i < data_len; i += 0x10) { len = 0x10; if ((i + len) > data_len) len = data_len - i; for (p = buffer, j = 0; j < len; j++, p += 3) sprintf(p, "%02x ", data[i + j]); fr_log(log, type, "%04x: %s", (int)i, buffer); } }
/* * This is wrapper for fr_log * Now users can call radiusd::log(level,msg) wich is the same * as calling fr_log from C code. */ static XS(XS_radiusd_log) { dXSARGS; if (items !=2) croak("Usage: radiusd::log(level, message)"); { int level; char *msg; level = (int) SvIV(ST(0)); msg = (char *) SvPV(ST(1), PL_na); /* * Because 'msg' is a 'char *', we don't want '%s', etc. * in it to give us printf-style vulnerabilities. */ fr_log(&default_log, level, "rlm_perl: %s", msg); } XSRETURN_NO; }
/** Handle a network control message callback for a new socket * * @param[in] ctx the network * @param[in] data the message * @param[in] data_size size of the data * @param[in] now the current time */ static void fr_network_socket_callback(void *ctx, void const *data, size_t data_size, UNUSED fr_time_t now) { fr_network_t *nr = ctx; fr_network_socket_t *s; fr_app_io_t const *app_io; size_t size; int num_messages; rad_assert(data_size == sizeof(s->listen)); if (data_size != sizeof(s->listen)) return; s = talloc_zero(nr, fr_network_socket_t); rad_assert(s != NULL); s->nr = nr; memcpy(&s->listen, data, sizeof(s->listen)); s->number = nr->num_sockets++; MEM(s->waiting = fr_heap_create(s, waiting_cmp, fr_channel_data_t, channel.heap_id)); talloc_set_destructor(s, _network_socket_free); /* * Put reasonable limits on the ring buffer size. Then * round it up to the nearest power of 2, which is * required by the ring buffer code. */ num_messages = s->listen->num_messages; if (num_messages < 8) num_messages = 8; size = s->listen->default_message_size * num_messages; if (!size) size = (1 << 17); /* * Allocate the ring buffer for messages and packets. */ s->ms = fr_message_set_create(s, num_messages, sizeof(fr_channel_data_t), size); if (!s->ms) { fr_log(nr->log, L_ERR, "Failed creating message buffers for network IO: %s", fr_strerror()); talloc_free(s); return; } app_io = s->listen->app_io; rad_assert(app_io->fd); s->fd = app_io->fd(s->listen->app_io_instance); s->filter = FR_EVENT_FILTER_IO; if (fr_event_fd_insert(nr, nr->el, s->fd, fr_network_read, NULL, fr_network_error, s) < 0) { PERROR("Failed adding new socket to network event loop"); talloc_free(s); return; } if (app_io->event_list_set) app_io->event_list_set(s->listen->app_io_instance, nr->el, nr); (void) rbtree_insert(nr->sockets, s); (void) rbtree_insert(nr->sockets_by_num, s); DEBUG3("Using new socket with FD %d", s->fd); }
/** Read a packet from the network. * * @param[in] el the event list. * @param[in] sockfd the socket which is ready to read. * @param[in] flags from kevent. * @param[in] ctx the network socket context. */ static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int flags, void *ctx) { int num_messages = 0; fr_network_socket_t *s = ctx; fr_network_t *nr = s->nr; ssize_t data_size; fr_channel_data_t *cd, *next; fr_time_t *recv_time; if (!fr_cond_assert(s->fd == sockfd)) return; DEBUG3("network read"); if (!s->cd) { cd = (fr_channel_data_t *) fr_message_reserve(s->ms, s->listen->default_message_size); if (!cd) { fr_log(nr->log, L_ERR, "Failed allocating message size %zd! - Closing socket", s->listen->default_message_size); fr_network_socket_dead(nr, s); return; } } else { cd = s->cd; } rad_assert(cd->m.data != NULL); rad_assert(cd->m.rb_size >= 256); next_message: /* * Poll this socket, but not too often. We have to go * service other sockets, too. */ if (num_messages > 16) { s->cd = cd; return; } cd->request.is_dup = false; cd->priority = PRIORITY_NORMAL; /* * Read data from the network. * * Return of 0 means "no data", which is fine for UDP. * For TCP, if an underlying read() on the TCP socket * returns 0, (which signals that the FD is no longer * usable) this function should return -1, so that the * network side knows that it needs to close the * connection. */ data_size = s->listen->app_io->read(s->listen->app_io_instance, &cd->packet_ctx, &recv_time, cd->m.data, cd->m.rb_size, &s->leftover, &cd->priority, &cd->request.is_dup); if (data_size == 0) { /* * Cache the message for later. This is * important for stream sockets, which can do * partial reads into the current buffer. We * need to be able to give the same buffer back * to the stream socket for subsequent reads. * * Since we have a message set for each * fr_io_socket_t, no "head of line" * blocking issues can happen for stream sockets. */ s->cd = cd; return; } /* * Error: close the connection, and remove the fr_listen_t */ if (data_size < 0) { // fr_log(nr->log, L_DBG_ERR, "error from transport read on socket %d", sockfd); fr_network_socket_dead(nr, s); return; } s->cd = NULL; DEBUG("Network received packet size %zd", data_size); nr->stats.in++; s->stats.in++; /* * Initialize the rest of the fields of the channel data. * * We always use "now" as the time of the message, as the * packet MAY be a duplicate packet magically resurrected * from the past. */ cd->m.when = fr_time(); cd->listen = s->listen; cd->request.recv_time = recv_time; /* * Nothing in the buffer yet. Allocate room for one * packet. */ if ((cd->m.data_size == 0) && (!s->leftover)) { (void) fr_message_alloc(s->ms, &cd->m, data_size); next = NULL; } else { /* * There are leftover bytes in the buffer, feed * them to the next round of reading. */ next = (fr_channel_data_t *) fr_message_alloc_reserve(s->ms, &cd->m, data_size, s->leftover, s->listen->default_message_size); if (!next) { fr_log(nr->log, L_ERR, "Failed reserving partial packet."); // @todo - probably close the socket... rad_assert(0 == 1); } } if (!fr_network_send_request(nr, cd)) { fr_log(nr->log, L_ERR, "Failed sending packet to worker"); fr_message_done(&cd->m); nr->stats.dropped++; s->stats.dropped++; } else { /* * One more packet sent to a worker. */ s->outstanding++; } /* * If there is a next message, go read it from the buffer. * * @todo - note that this calls read(), even if the * app_io has paused the reader. We likely want to be * able to check that, too. We might just remove this * "goto"... */ if (next) { cd = next; num_messages++; goto next_message; } }