1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28/* Used by tests like divert-to.sh */ 29 30#include <errno.h> 31#include <stdlib.h> 32#include <stdbool.h> 33#include <err.h> 34#include <sysexits.h> 35#include <string.h> 36 37#include <sys/types.h> 38#include <sys/socket.h> 39#include <netinet/in.h> 40#include <netinet/ip.h> 41 42 43struct context { 44 unsigned short divert_port; 45 bool divert_back; 46 47 int fd; 48 struct sockaddr_in sin; 49 socklen_t sin_len; 50 char pkt[IP_MAXPACKET]; 51 ssize_t pkt_n; 52}; 53 54static void 55init(struct context *c) 56{ 57 c->fd = socket(PF_DIVERT, SOCK_RAW, 0); 58 if (c->fd == -1) 59 errx(EX_OSERR, "init: Cannot create divert socket."); 60 61 memset(&c->sin, 0, sizeof(c->sin)); 62 c->sin.sin_family = AF_INET; 63 c->sin.sin_port = htons(c->divert_port); 64 c->sin.sin_addr.s_addr = INADDR_ANY; 65 c->sin_len = sizeof(struct sockaddr_in); 66 67 if (bind(c->fd, (struct sockaddr *) &c->sin, c->sin_len) != 0) 68 errx(EX_OSERR, "init: Cannot bind divert socket."); 69} 70 71static ssize_t 72recv_pkt(struct context *c) 73{ 74 fd_set readfds; 75 struct timeval timeout; 76 int s; 77 78 FD_ZERO(&readfds); 79 FD_SET(c->fd, &readfds); 80 timeout.tv_sec = 3; 81 timeout.tv_usec = 0; 82 83 s = select(c->fd + 1, &readfds, 0, 0, &timeout); 84 if (s == -1) 85 errx(EX_IOERR, "recv_pkt: select() errors."); 86 if (s != 1) // timeout 87 return -1; 88 89 c->pkt_n = recvfrom(c->fd, c->pkt, sizeof(c->pkt), 0, 90 (struct sockaddr *) &c->sin, &c->sin_len); 91 if (c->pkt_n == -1) 92 errx(EX_IOERR, "recv_pkt: recvfrom() errors."); 93 94 return (c->pkt_n); 95} 96 97static void 98send_pkt(struct context *c) 99{ 100 ssize_t n; 101 char errstr[32]; 102 103 n = sendto(c->fd, c->pkt, c->pkt_n, 0, 104 (struct sockaddr *) &c->sin, c->sin_len); 105 if (n == -1) { 106 strerror_r(errno, errstr, sizeof(errstr)); 107 errx(EX_IOERR, "send_pkt: sendto() errors: %d %s.", errno, errstr); 108 } 109 if (n != c->pkt_n) 110 errx(EX_IOERR, "send_pkt: sendto() sent %zd of %zd bytes.", 111 n, c->pkt_n); 112} 113 114int 115main(int argc, char *argv[]) 116{ 117 struct context c; 118 int npkt; 119 120 if (argc < 2) 121 errx(EX_USAGE, 122 "Usage: %s <divert-port> [divert-back]", argv[0]); 123 124 memset(&c, 0, sizeof(struct context)); 125 126 c.divert_port = (unsigned short) strtol(argv[1], NULL, 10); 127 if (c.divert_port == 0) 128 errx(EX_USAGE, "divert port is not defined."); 129 130 if (argc >= 3 && strcmp(argv[2], "divert-back") == 0) 131 c.divert_back = true; 132 133 134 init(&c); 135 136 npkt = 0; 137 while (recv_pkt(&c) > 0) { 138 if (c.divert_back) 139 send_pkt(&c); 140 npkt++; 141 if (npkt >= 10) 142 break; 143 } 144 145 if (npkt != 1) 146 errx(EXIT_FAILURE, "%d: npkt=%d.", c.divert_port, npkt); 147 148 return EXIT_SUCCESS; 149} 150