/* * Copyright (c) 2018-2024 OARC, Inc. * All rights reserved. * * This file is part of dnsjit. * * dnsjit is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * dnsjit is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with dnsjit. If not, see . */ #include "config.h" #include "output/udpcli.h" #include "core/assert.h" #include "core/object/dns.h" #include "core/object/payload.h" #include #include #include #include #include static core_log_t _log = LOG_T_INIT("output.udpcli"); static output_udpcli_t _defaults = { LOG_T_INIT_OBJ("output.udpcli"), 0, 0, -1, { 0 }, 0, { 0 }, CORE_OBJECT_PAYLOAD_INIT(0), 0, { 5, 0 }, 1 }; core_log_t* output_udpcli_log() { return &_log; } void output_udpcli_init(output_udpcli_t* self) { mlassert_self(); *self = _defaults; self->pkt.payload = self->recvbuf; } void output_udpcli_destroy(output_udpcli_t* self) { mlassert_self(); if (self->fd > -1) { shutdown(self->fd, SHUT_RDWR); close(self->fd); } } int output_udpcli_connect(output_udpcli_t* self, const char* host, const char* port) { struct addrinfo* addr; int err; mlassert_self(); lassert(host, "host is nil"); lassert(port, "port is nil"); if (self->fd > -1) { lfatal("already connected"); } if ((err = getaddrinfo(host, port, 0, &addr))) { lcritical("getaddrinfo(%s, %s) error %s", host, port, gai_strerror(err)); return -1; } if (!addr) { lcritical("getaddrinfo failed, no address returned"); return -1; } memcpy(&self->addr, addr->ai_addr, addr->ai_addrlen); self->addr_len = addr->ai_addrlen; freeaddrinfo(addr); if ((self->fd = socket(((struct sockaddr*)&self->addr)->sa_family, SOCK_DGRAM, 0)) < 0) { lcritical("socket() error %s", core_log_errstr(errno)); return -2; } return 0; } int output_udpcli_nonblocking(output_udpcli_t* self) { int flags; mlassert_self(); if (self->fd < 0) { lfatal("not connected"); } flags = fcntl(self->fd, F_GETFL); if (flags != -1) { flags = flags & O_NONBLOCK ? 1 : 0; } return flags; } int output_udpcli_set_nonblocking(output_udpcli_t* self, int nonblocking) { int flags; mlassert_self(); if (self->fd < 0) { lfatal("not connected"); } if ((flags = fcntl(self->fd, F_GETFL)) == -1) { lcritical("fcntl(FL_GETFL) error %s", core_log_errstr(errno)); return -1; } if (nonblocking) { flags |= O_NONBLOCK; self->blocking = 0; } else { flags &= ~O_NONBLOCK; self->blocking = 1; } if (fcntl(self->fd, F_SETFL, flags | O_NONBLOCK)) { lcritical("fcntl(FL_SETFL, %x) error %s", flags, core_log_errstr(errno)); return -1; } return 0; } static void _receive(output_udpcli_t* self, const core_object_t* obj) { const uint8_t* payload; size_t len, sent; mlassert_self(); for (; obj;) { switch (obj->obj_type) { case CORE_OBJECT_DNS: obj = obj->obj_prev; continue; case CORE_OBJECT_PAYLOAD: payload = ((core_object_payload_t*)obj)->payload; len = ((core_object_payload_t*)obj)->len; break; default: return; } sent = 0; for (;;) { ssize_t ret = sendto(self->fd, payload + sent, len - sent, 0, (struct sockaddr*)&self->addr, self->addr_len); if (ret > -1) { sent += ret; if (sent < len) continue; self->pkts++; return; } switch (errno) { case EAGAIN: #if EAGAIN != EWOULDBLOCK case EWOULDBLOCK: #endif continue; default: break; } break; } self->errs++; break; } } core_receiver_t output_udpcli_receiver(output_udpcli_t* self) { mlassert_self(); if (self->fd < 0) { lfatal("not connected"); } return (core_receiver_t)_receive; } static const core_object_t* _produce(output_udpcli_t* self) { ssize_t n; mlassert_self(); for (;;) { n = recvfrom(self->fd, self->recvbuf, sizeof(self->recvbuf), 0, 0, 0); if (n > -1) { break; } switch (errno) { case EAGAIN: #if EAGAIN != EWOULDBLOCK case EWOULDBLOCK: #endif self->pkt.len = 0; return (core_object_t*)&self->pkt; default: break; } self->errs++; break; } if (n < 1) { return 0; } self->pkts_recv++; self->pkt.len = n; return (core_object_t*)&self->pkt; } static const core_object_t* _produce_block(output_udpcli_t* self) { ssize_t n; struct pollfd p; int to; mlassert_self(); p.fd = self->fd; p.events = POLLIN; p.revents = 0; to = (self->timeout.sec * 1e3) + (self->timeout.nsec / 1e6); // NOSONAR if (!to) { to = 1; } n = poll(&p, 1, to); if (n < 0 || (p.revents & (POLLERR | POLLHUP | POLLNVAL))) { self->errs++; return 0; } if (!n || !(p.revents & POLLIN)) { self->pkt.len = 0; return (core_object_t*)&self->pkt; } for (;;) { n = recvfrom(self->fd, self->recvbuf, sizeof(self->recvbuf), 0, 0, 0); if (n > -1) { break; } switch (errno) { case EAGAIN: #if EAGAIN != EWOULDBLOCK case EWOULDBLOCK: #endif self->pkt.len = 0; return (core_object_t*)&self->pkt; default: break; } self->errs++; break; } if (n < 1) { return 0; } self->pkts_recv++; self->pkt.len = n; return (core_object_t*)&self->pkt; } core_producer_t output_udpcli_producer(output_udpcli_t* self) { mlassert_self(); if (self->fd < 0) { lfatal("not connected"); } if (self->blocking) { return (core_producer_t)_produce_block; } return (core_producer_t)_produce; }