Blob


1 /*
2 * Copyright (c) 2023 Omar Polo <op@openbsd.org>
3 * Copyright (c) 2003-2004 Daniel Hartmeier
4 * All rights reserved.
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 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 */
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <netdb.h>
44 #include <poll.h>
45 #include <signal.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <time.h>
51 #include <unistd.h>
53 #include "icb.h"
54 #include "irc.h"
56 void icbirc_quit(void);
57 int sync_write(int, const char *, int);
58 static void usage(void);
60 time_t t;
61 unsigned long bytes_in, bytes_out;
63 static void
64 usage(void)
65 {
66 extern char *__progname;
68 fprintf(stderr, "usage: %s host[:port][/room]\n",
69 getprogname());
70 exit(1);
71 }
73 int
74 main(int argc, char *argv[])
75 {
76 struct pollfd pfds[2], *pirc, *picb;
77 struct addrinfo hints, *res, *res0;
78 const char *host, *cause = NULL;
79 char *port, *room;
80 int ch, error, save_errno;
81 int listen_fd = -1, server_fd = -1;
83 #ifdef __OpenBSD__
84 if (pledge("stdio inet dns", NULL) == -1)
85 err(1, "pledge");
86 #endif /* __OpenBSD__ */
88 while ((ch = getopt(argc, argv, "")) != -1) {
89 switch (ch) {
90 default:
91 usage();
92 }
93 }
94 argc -= optind;
95 argv += optind;
97 if (argc != 1)
98 usage();
100 signal(SIGPIPE, SIG_IGN);
101 if (fcntl(0, F_SETFL, fcntl(listen_fd, F_GETFL) | O_NONBLOCK))
102 err(1, "fcntl");
104 if ((port = strchr(argv[0], ':')) != NULL) {
105 *port++ = '\0';
106 if ((room = strchr(port, '/')) != NULL) {
107 *room++ = '\0';
108 strlcpy(irc_pass, room, sizeof(irc_pass));
110 } else if ((room = strchr(argv[0], '/')) != NULL) {
111 *room++ = '\0';
112 strlcpy(irc_pass, room, sizeof(irc_pass));
114 host = argv[0];
115 if (port == NULL)
116 port = "7326";
118 t = time(NULL);
120 irc_send_notice(1, "*** Connecting to server %s:%s", host, port);
122 memset(&hints, 0, sizeof(hints));
123 hints.ai_family = AF_UNSPEC;
124 hints.ai_socktype = SOCK_STREAM;
125 error = getaddrinfo(host, port, &hints, &res0);
126 if (error)
127 errx(1, "can't resolve %s: %s", host, gai_strerror(error));
129 for (res = res0; res; res = res->ai_next) {
130 server_fd = socket(res->ai_family, res->ai_socktype,
131 res->ai_protocol);
132 if (server_fd == -1) {
133 cause = "socket";
134 continue;
137 if (connect(server_fd, res->ai_addr, res->ai_addrlen) == -1) {
138 cause = "connect";
139 save_errno = errno;
140 close(server_fd);
141 errno = save_errno;
142 server_fd = -1;
143 continue;
146 break;
148 freeaddrinfo(res0);
149 if (server_fd == -1)
150 err(1, "%s", cause);
152 if (fcntl(server_fd, F_SETFL, fcntl(server_fd, F_GETFL) | O_NONBLOCK))
153 err(1, "fcntl");
155 #ifdef __OpenBSD__
156 /* drop inet dns */
157 if (pledge("stdio", NULL) == -1)
158 err(1, "pledge");
159 #endif /* __OpenBSD__ */
161 irc_send_notice(1, "*** Connected");
162 icb_init();
164 memset(&pfds, 0, sizeof(pfds));
165 pirc = &pfds[0];
166 picb = &pfds[1];
168 pirc->fd = 0;
169 pirc->events = POLLIN;
171 picb->fd = server_fd;
172 picb->events = POLLIN;
174 for (;;) {
175 char buf[65535];
176 int len;
178 if (poll(pfds, 2, 10000) == -1) {
179 if (errno == EINTR)
180 continue;
181 err(1, "poll");
184 if (picb->revents & (POLLIN|POLLNVAL|POLLHUP)) {
185 len = read(server_fd, buf, sizeof(buf));
186 if (len < 0) {
187 if (errno == EINTR)
188 continue;
189 warnx("read");
190 len = 0;
192 if (len == 0) {
193 warnx("connection closed by server");
194 irc_send_notice(1, "*** Connection "
195 "closed by server");
196 break;
198 icb_recv(buf, len, 1, server_fd);
199 bytes_in += len;
202 if (pirc->revents & (POLLIN|POLLNVAL|POLLHUP)) {
203 len = read(0, buf, sizeof(buf));
204 if (len < 0) {
205 if (errno == EINTR)
206 continue;
207 warnx("read");
208 len = 0;
210 if (len == 0) {
211 warnx("connection closed by client");
212 break;
214 irc_recv(buf, len, 1, server_fd);
215 bytes_out += len;
219 icbirc_quit();
222 void
223 icbirc_quit(void)
225 irc_send_notice(1, "*** Closing connection "
226 "(%u seconds, %lu:%lu bytes)",
227 time(NULL) - t, bytes_out, bytes_in);
228 exit(0);
231 int
232 sync_write(int fd, const char *buf, int len)
234 int off = 0;
236 while (len > off) {
237 fd_set writefds;
238 struct timeval tv;
239 int r;
241 FD_ZERO(&writefds);
242 FD_SET(fd, &writefds);
243 memset(&tv, 0, sizeof(tv));
244 tv.tv_sec = 10;
245 r = select(fd + 1, NULL, &writefds, NULL, &tv);
246 if (r < 0) {
247 if (errno != EINTR) {
248 perror("select");
249 return (1);
251 continue;
253 if (r > 0 && FD_ISSET(fd, &writefds)) {
254 r = write(fd, buf + off, len - off);
255 if (r < 0) {
256 perror("write");
257 return (1);
259 off += r;
262 return (0);