1 /*
2  * ircio.c: A quaint little program to make irc life PING free
3  *
4  * Written By Michael Sandrof
5  *
6  * Copyright (c) 1990 Michael Sandrof.
7  * Copyright (c) 1991, 1992 Troy Rollo.
8  * Copyright (c) 1992-2014 Matthew R. Green.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "irc.h"
36 IRCII_RCSID("@(#)$eterna: ircio.c,v 2.34 2014/07/11 09:56:27 mrg Exp $");
37 
38 #include <errno.h>
39 
40 #include "defs.h"
41 #include "newio.h"
42 
43 #ifdef HAVE_SYS_UN_H
44 #include <sys/un.h>
45 static	int	connect_to_unix(char *);
46 #endif /* HAVE_SYS_UN_H */
47 
48 #undef NON_BLOCKING
49 
50 	void	new_free(char **);
51 	void	*new_malloc(size_t);
52 static	int	connect_by_number(char *, char *);
53 	int	main(int, char *[], char *[]);
54 
55 void	*
new_malloc(size_t size)56 new_malloc(size_t size)
57 {
58 	void	*ptr;
59 
60 	if ((ptr = malloc(size)) == NULL)
61 	{
62 		printf("-1 0\n");
63 		exit(1);
64 	}
65 	return (ptr);
66 }
67 
68 /*
69  * new_free:  Why do this?  Why not?  Saves me a bit of trouble here and
70  * there
71  */
72 void
new_free(char ** ptr)73 new_free(char **ptr)
74 {
75 	if (*ptr)
76 	{
77 		free(*ptr);
78 		*ptr = 0;
79 	}
80 }
81 
82 #ifdef DEBUG
83 void
debug(int level,char * fmt,...)84 debug(int level, char *fmt, ...)
85 {
86 }
87 #endif
88 
89 ssize_t
ssl_read(SslInfo * info,int fd,void * buf,size_t buflen)90 ssl_read(SslInfo *info, int fd, void *buf, size_t buflen)
91 {
92 	return read(fd, buf, buflen);
93 }
94 
95 /*
96  * connect_by_number Performs a connecting to socket 'service' on host
97  * 'host'.  Host can be a hostname or ip-address.  If 'host' is null, the
98  * local host is assumed.   The parameter full_hostname will, on return,
99  * contain the expanded hostname (if possible).  Note that full_hostname is a
100  * pointer to a char *, and is allocated by connect_by_numbers()
101  *
102  * Errors:
103  *
104  * -1 get service failed
105  *
106  * -2 get host failed
107  *
108  * -3 socket call failed
109  *
110  * -4 connect call failed
111  */
112 static	int
connect_by_number(char * service,char * host)113 connect_by_number(char *service, char *host)
114 {
115 	int	s = -1, err;
116 	char	buf[256];
117 	struct	addrinfo hints, *res = 0, *res0 = 0;
118 
119 	if (host == NULL)
120 	{
121 		gethostname(buf, sizeof(buf));
122 		host = buf;
123 	}
124 	memset(&hints, 0, sizeof hints);
125 	hints.ai_flags = 0;
126 	hints.ai_protocol = 0;
127 	hints.ai_addrlen = 0;
128 	hints.ai_canonname = NULL;
129 	hints.ai_addr = NULL;
130 	hints.ai_next = NULL;
131 	hints.ai_socktype = SOCK_STREAM;
132 	hints.ai_family = AF_UNSPEC;
133 	err = getaddrinfo(host, service, &hints, &res0);
134 	if (err != 0)
135 		return -2;
136 	for (res = res0; res; res = res->ai_next)
137 	{
138 		err = 0;
139 		if ((s = socket(res->ai_family, res->ai_socktype,
140 		    res->ai_protocol)) < 0)
141 			continue;
142 		set_socket_options(s);
143 		err = connect(s, res->ai_addr, res->ai_addrlen);
144 		if (err == 0)
145 			break;
146 		close(s);
147 	}
148 	freeaddrinfo(res0);
149 	if (err)
150 		return -4;
151 	return (s);
152 }
153 
154 /*
155  * ircio: This little program connects to the server (given as arg 1) on
156  * the given port (given as arg 2).  It then accepts input from stdin and
157  * sends it to that server. Likewise, it reads stuff sent from the server and
158  * sends it to stdout.  Simple?  Yes, it is.  But wait!  There's more!  It
159  * also intercepts server PINGs and automatically responds to them.   This
160  * frees up the process that starts ircio (such as IRCII) to pause without
161  * fear of being pooted off the net.
162  *
163  * Future enhancements: No-blocking io.  It will either discard or dynamically
164  * buffer anything that would block.
165  */
166 int
main(int argc,char * argv[],char * envp[])167 main(int argc, char *argv[], char *envp[])
168 {
169 	int	des;
170 	fd_set	rd;
171 	int	done = 0,
172 		c;
173 	char	*ptr,
174 		lbuf[BUFSIZ + 1],
175 		pong[BUFSIZ + 1];
176 #ifdef NON_BLOCKING
177 	char	block_buffer[BUFSIZ + 1];
178 	fd_set	*wd_ptr = NULL,
179 		wd;
180 	int	wrote;
181 #endif /* NON_BLOCKING */
182 
183 	if (argc < 3)
184 		exit(1);
185 #ifdef	SOCKS
186 	SOCKSinit(*argv);
187 #endif /* SOCKS */
188 #ifdef HAVE_SYS_UN_H
189 	if (*argv[1] == '/')
190 		des = connect_to_unix(argv[1]);
191 	else
192 #endif /* HAVE_SYS_UN_H */
193 		des = connect_by_number(argv[2], argv[1]);
194 	if (des < 0)
195 		exit(des);
196 	fflush(stdout);
197 
198 	(void) MY_SIGNAL(SIGTERM, (sigfunc *) SIG_IGN, 0);
199 	(void) MY_SIGNAL(SIGSEGV, (sigfunc *) SIG_IGN, 0);
200 	(void) MY_SIGNAL(SIGBUS, (sigfunc *) SIG_IGN, 0);
201 	(void) MY_SIGNAL(SIGPIPE, (sigfunc *) SIG_IGN, 0);
202 #ifdef SIGWINCH
203 	(void) MY_SIGNAL(SIGWINCH, (sigfunc *) SIG_IGN, 0);
204 #endif /* SIGWINCH */
205 
206 #ifdef NON_BLOCKING
207 	if (fcntl(1, F_SETFL, FNDELAY))
208 		exit(1);
209 #endif /* NON_BLOCKING */
210 	while (!done)
211 	{
212 		fflush(stderr);
213 		FD_ZERO(&rd);
214 		FD_SET(0, &rd);
215 		FD_SET(des, &rd);
216 #ifdef NON_BLOCKING
217 		if (wd_ptr)
218 		{
219 			FD_ZERO(wd_ptr);
220 			FD_SET(1, wd_ptr);
221 		}
222 		switch (new_select(&rd, wd_ptr, NULL))
223 		{
224 #else
225 		switch (new_select(&rd, NULL, NULL))
226 		{
227 #endif /* NON_BLOCKING */
228 		case -1:
229 		case 0:
230 			break;
231 		default:
232 #ifdef NON_BLOCKING
233 			if (wd_ptr)
234 			{
235 				if (FD_ISSET(1, wd_ptr))
236 				{
237 					c = strlen(block_buffer);
238 					if ((wrote = write(1, block_buffer,
239 							c)) == -1)
240 					{
241 						wd_ptr = &wd;
242 					}
243 					else if (wrote < c)
244 					{
245 						strcpy(block_buffer,
246 							&(block_buffer[wrote]));
247 						wd_ptr = &wd;
248 					}
249 					else
250 						wd_ptr = NULL;
251 				}
252 			}
253 #endif /* NON_BLOCKING */
254 			if (FD_ISSET(0, &rd))
255 			{
256 				c = dgets(UP(lbuf), BUFSIZ, 0);
257 				switch (c) {
258 				case -1:
259 				case 0:
260 					done = 1;
261 					break;
262 				default:
263 					if (write(des, lbuf, (size_t)c) != c)
264 						done = 1;
265 				}
266 			}
267 			if (FD_ISSET(des, &rd))
268 			{
269 				c = dgets(UP(lbuf), BUFSIZ, des);
270 				switch (c) {
271 				case -1:
272 				case 0:
273 					done = 1;
274 					break;
275 				default:
276 					if (strncmp(lbuf, "PING ", 5) == 0)
277 					{
278 						if ((ptr = (char *)
279 						    my_index(lbuf, ' ')) != NULL)
280 						{
281 							snprintf(pong, sizeof pong, "PONG user@host %s\n", ptr + 1);
282 							if (write(des, pong, strlen(pong)) != strlen(pong))
283 								done = 1;
284 						}
285 					}
286 					else
287 					{
288 #ifdef NON_BLOCKING
289 						if ((wrote = write(1, lbuf,
290 								(size_t)c)) == -1)
291 							wd_ptr = &wd;
292 						else if (wrote < c)
293 						{
294 							strcpy(block_buffer,
295 							    &(lbuf[wrote]));
296 							wd_ptr = &wd;
297 						}
298 #else
299 						if (write(1, lbuf, (size_t)c) != c)
300 							done = 1;
301 #endif /* NON_BLOCKING */
302 					}
303 				}
304 			}
305 		}
306 	}
307 	return 0;
308 }
309 
310 
311 #ifdef HAVE_SYS_UN_H
312 /*
313  * Connect to a UNIX domain socket. Only works for servers.
314  * submitted by Avalon for use with server 2.7.2 and beyond.
315  */
316 static	int
317 connect_to_unix(char *path)
318 {
319 	struct	sockaddr_un	un;
320 	int	sock;
321 
322 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
323 
324 	un.sun_family = AF_UNIX;
325 	strcpy(un.sun_path, path);
326 	if (connect(sock, (struct sockaddr *)&un, (int)strlen(path)+2) == -1)
327 	{
328 		new_close(sock);
329 		return -1;
330 	}
331 	return sock;
332 }
333 #endif /* HAVE_SYS_UN_H */
334