1/* $OpenBSD: netcat.c,v 1.57 2002/12/30 18:00:18 stevesk Exp $ */
2/*
3 * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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 * 3. The name of the author may not be used to endorse or promote products
15 *   derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <arpa/telnet.h>
30#include <netinet/in.h>
31#include <sys/socket.h>
32#include <sys/termios.h>
33#include <sys/time.h>
34#include <sys/types.h>
35#include <sys/un.h>
36#include <err.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <netdb.h>
40#include <poll.h>
41#include <stdarg.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
48void    readwrite(int);
49int     remote_connect(char *, char *, struct addrinfo);
50
51struct  termios saved_ios;
52void    raw_term();
53void    restore_term();
54
55char    progname[256];
56void    usage(int);
57
58int
59main(int argc, char *argv[])
60{
61    int ch, s, ret;
62    char *host, *port, *endp;
63    struct addrinfo hints;
64    socklen_t len;
65
66    ret = 1;
67    s = 0;
68    host = NULL;
69    port = NULL;
70    endp = NULL;
71
72    strncpy(progname, argv[0], sizeof progname);
73
74    /* Cruft to make sure options are clean, and used properly. */
75    if (argc == 2) {
76        host = "localhost";
77        port = argv[1];
78    } else if (argc == 3) {
79        host = argv[1];
80        port = argv[2];
81    } else {
82        usage(1);
83    }
84
85    if (!isatty(STDIN_FILENO))
86        errx(1, "not attached to a terminal");
87
88    raw_term();
89
90    /* Initialize addrinfo structure */
91    memset(&hints, 0, sizeof(struct addrinfo));
92    hints.ai_family = AF_UNSPEC;
93    hints.ai_socktype = SOCK_STREAM;
94    hints.ai_protocol = IPPROTO_TCP;
95
96    s = remote_connect(host, port, hints);
97    ret = 0;
98    readwrite(s);
99
100    if (s)
101        close(s);
102
103    exit(ret);
104}
105
106/*
107 * remote_connect()
108 * Return's a socket connected to a remote host. Properly bind's to a local
109 * port or source address if needed. Return's -1 on failure.
110 */
111int
112remote_connect(char *host, char *port, struct addrinfo hints)
113{
114    struct addrinfo *res, *res0;
115    int s, error;
116
117    if ((error = getaddrinfo(host, port, &hints, &res)))
118        errx(1, "getaddrinfo: %s", gai_strerror(error));
119
120    res0 = res;
121    do {
122        if ((s = socket(res0->ai_family, res0->ai_socktype,
123                        res0->ai_protocol)) < 0)
124            continue;
125
126        if (connect(s, res0->ai_addr, res0->ai_addrlen) == 0)
127            break;
128
129        close(s);
130        s = -1;
131    } while ((res0 = res0->ai_next) != NULL);
132
133    freeaddrinfo(res);
134
135    return (s);
136}
137
138/*
139 * readwrite()
140 * Loop that selects on the network file descriptor and stdin.
141 * Changed from poll() by Ali Saidi to make work on Mac OS X >= 10.4
142 */
143void
144readwrite(int nfd)
145{
146    fd_set read_fds;
147    char buf[BUFSIZ];
148    int wfd = fileno(stdin), n, ret, max_fd;
149    int lfd = fileno(stdout);
150    int escape = 0;
151    struct timeval timeout;
152
153    if (nfd == -1)
154        return;
155
156    max_fd = nfd + 1;
157
158    while (1) {
159        FD_ZERO(&read_fds);
160        FD_SET(wfd, &read_fds);
161        FD_SET(nfd, &read_fds);
162        timeout.tv_sec = 1;
163        timeout.tv_usec = 0;
164
165        n = select(max_fd, &read_fds, NULL, NULL, &timeout);
166        if (n < 0) {
167            close(nfd);
168            perror("Select Error:");
169        }
170
171        if (n == 0) {
172            if (read(nfd, buf, 0) < 0)
173                return;
174            continue;
175        }
176
177        if (read(nfd, buf, 0) < 0)
178            return;
179
180        if (FD_ISSET(nfd, &read_fds)) {
181            if ((n = read(nfd, buf, sizeof(buf))) < 0)
182                return;
183            else if (n == 0) {
184                shutdown(nfd, SHUT_RD);
185                return;
186            } else {
187                if ((ret = atomicio(write, lfd, buf, n)) != n)
188                    return;
189            }
190        }
191
192        if (FD_ISSET(wfd, &read_fds)) {
193            if ((n = read(wfd, buf, sizeof(buf))) < 0)
194                return;
195            else if (n == 0) {
196                shutdown(nfd, SHUT_WR);
197            } else {
198                if (escape) {
199                    char buf2[] = "~";
200                    if (*buf == '.') {
201                        printf("quit!\n");
202                        return;
203                    }
204                    escape = 0;
205                    if (*buf != '~' &&
206                        (ret = atomicio(write, nfd, buf2, 1)) != n)
207                        return;
208                } else {
209                    escape = (*buf == '~');
210                    if (escape)
211                        continue;
212                }
213
214                if ((ret = atomicio(write, nfd, buf, n)) != n)
215                    return;
216            }
217        }
218    } // while
219}
220
221void
222usage(int ret)
223{
224    fprintf(stderr, "usage: %s hostname port\n", progname);
225    if (ret)
226        exit(1);
227}
228
229/*
230 * Copyright (c) 1995,1999 Theo de Raadt.  All rights reserved.
231 * All rights reserved.
232 *
233 * Redistribution and use in source and binary forms, with or without
234 * modification, are permitted provided that the following conditions
235 * are met:
236 * 1. Redistributions of source code must retain the above copyright
237 *    notice, this list of conditions and the following disclaimer.
238 * 2. Redistributions in binary form must reproduce the above copyright
239 *    notice, this list of conditions and the following disclaimer in the
240 *    documentation and/or other materials provided with the distribution.
241 *
242 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
243 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
244 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
245 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
246 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
247 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
248 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
249 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
250 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
251 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
252 */
253
254/*
255 * ensure all of data on socket comes through. f==read || f==write
256 */
257ssize_t
258atomicio(ssize_t (*f) (), int fd, void *_s, size_t n)
259{
260    char *s = _s;
261    ssize_t res, pos = 0;
262
263    while (n > pos) {
264        res = (f) (fd, s + pos, n - pos);
265        switch (res) {
266          case -1:
267            if (errno == EINTR || errno == EAGAIN)
268                continue;
269          case 0:
270            return (res);
271          default:
272            pos += res;
273        }
274    }
275    return (pos);
276}
277
278/*
279 * Copyright (c) 2003 Nathan L. Binkert <binkertn@umich.edu>
280 *
281 * Permission to use, copy, modify, and distribute this software for any
282 * purpose with or without fee is hereby granted, provided that the above
283 * copyright notice and this permission notice appear in all copies.
284 *
285 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
286 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
287 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
288 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
289 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
290 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
291 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
292 */
293
294void
295raw_term()
296{
297    struct termios ios;
298
299    if (tcgetattr(STDIN_FILENO, &ios) < 0)
300        errx(1, "tcgetagttr\n");
301
302    memcpy(&saved_ios, &ios, sizeof(struct termios));
303
304    ios.c_iflag &= ~(ISTRIP|ICRNL|IGNCR|ICRNL|IXOFF|IXON);
305    ios.c_oflag &= ~(OPOST);
306    ios.c_oflag &= (ONLCR);
307    ios.c_lflag &= ~(ISIG|ICANON|ECHO);
308    ios.c_cc[VMIN] = 1;
309    ios.c_cc[VTIME] = 0;
310
311    if (tcsetattr(STDIN_FILENO, TCSANOW, &ios) < 0)
312        errx(1, "tcsetattr\n");
313
314    atexit(restore_term);
315}
316
317void
318restore_term()
319{
320    tcsetattr(STDIN_FILENO, TCSANOW, &saved_ios);
321}
322