master xplshn/aruu / cmd / net / netcat.c
  1/* See LICENSE file for copyright and license details. */
  2
  3
  4#include <sys/socket.h>
  5#include <sys/types.h>
  6
  7#include <arpa/inet.h>
  8#include <netinet/in.h>
  9
 10#include <errno.h>
 11#include <fcntl.h>
 12#include <netdb.h>
 13#include <poll.h>
 14#include <stdio.h>
 15#include <stdlib.h>
 16#include <string.h>
 17#include <unistd.h>
 18
 19#include "util.h"
 20
 21static void
 22usage(void)
 23{
 24	eprintf("usage: %s [-lu] [-p localport] [host] [port]\n", argv0);
 25}
 26
 27static int
 28resolve(const char *host, const char *port, int family, int socktype,
 29	int passive, struct sockaddr_storage *addr, socklen_t *addrlen)
 30{
 31	struct addrinfo hints, *res;
 32	int r;
 33
 34	memset(&hints, 0, sizeof(hints));
 35	hints.ai_family = family;
 36	hints.ai_socktype = socktype;
 37	if (passive)
 38		hints.ai_flags = AI_PASSIVE;
 39
 40	if ((r = getaddrinfo(host, port, &hints, &res)) != 0) {
 41		weprintf("getaddrinfo: %s\n", gai_strerror(r));
 42		return -1;
 43	}
 44
 45	memcpy(addr, res->ai_addr, res->ai_addrlen);
 46	*addrlen = res->ai_addrlen;
 47	freeaddrinfo(res);
 48	return 0;
 49}
 50
 51// ?man netcat: read and write data across network connections
 52// ?man arguments: host] [port
 53// ?man arbitrary data transmission over tcp or udp
 54int
 55main(int argc, char *argv[])
 56{
 57	struct sockaddr_storage local_addr, remote_addr;
 58	socklen_t local_len = sizeof(local_addr),
 59		  remote_len = sizeof(remote_addr);
 60	struct pollfd fds[2];
 61	int listenfd = -1, sockfd = -1;
 62	int lflag = 0;
 63	int uflag = 0;
 64	char *port = NULL;
 65	char *host = NULL;
 66	char *local_port = NULL;
 67	int socktype;
 68	int n, opt;
 69	char buf[BUFSIZ];
 70
 71	ARGBEGIN
 72	{
 73	// ?man -l: list in long format
 74	case 'l':
 75		lflag = 1;
 76		break;
 77	// ?man -p:str: preserve file attributes
 78	case 'p':
 79		local_port = EARGF(usage());
 80		break;
 81	// ?man -u: unbuffered output
 82	case 'u':
 83		uflag = 1;
 84		break;
 85	default:
 86		usage();
 87	}
 88	ARGEND
 89
 90	socktype = uflag ? SOCK_DGRAM : SOCK_STREAM;
 91
 92	if (lflag) {
 93		/* server mode */
 94		if (!local_port) {
 95			if (argc == 1) {
 96				local_port = argv[0];
 97				argc = 0;
 98			} else {
 99				usage();
100			}
101		}
102		memset(&local_addr, 0, sizeof(local_addr));
103		if (resolve(NULL, local_port, AF_UNSPEC, socktype, 1,
104			    &local_addr, &local_len) < 0)
105			return 1;
106
107		listenfd = socket(local_addr.ss_family, socktype, 0);
108		if (listenfd < 0)
109			eprintf("socket:");
110
111		opt = 1;
112		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt,
113			   sizeof(opt));
114
115		if (bind(listenfd, (struct sockaddr *)&local_addr, local_len) <
116		    0)
117			eprintf("bind:");
118
119		if (!uflag) {
120			if (listen(listenfd, 5) < 0)
121				eprintf("listen:");
122			sockfd = accept(listenfd,
123					(struct sockaddr *)&remote_addr,
124					&remote_len);
125			if (sockfd < 0)
126				eprintf("accept:");
127			close(listenfd);
128		} else {
129			sockfd = listenfd;
130			n = recvfrom(sockfd, buf, sizeof(buf), MSG_PEEK,
131				     (struct sockaddr *)&remote_addr,
132				     &remote_len);
133			if (n < 0)
134				eprintf("recvfrom:");
135			if (connect(sockfd, (struct sockaddr *)&remote_addr,
136				    remote_len) < 0)
137				eprintf("connect:");
138		}
139	} else {
140		/* client mode */
141		if (argc != 2)
142			usage();
143		host = argv[0];
144		port = argv[1];
145
146		memset(&remote_addr, 0, sizeof(remote_addr));
147		if (resolve(host, port, AF_UNSPEC, socktype, 0, &remote_addr,
148			    &remote_len) < 0)
149			return 1;
150
151		sockfd = socket(remote_addr.ss_family, socktype, 0);
152		if (sockfd < 0)
153			eprintf("socket:");
154
155		if (local_port) {
156			memset(&local_addr, 0, sizeof(local_addr));
157			if (resolve(NULL, local_port, remote_addr.ss_family,
158				    socktype, 1, &local_addr, &local_len) < 0)
159				return 1;
160			opt = 1;
161			setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt,
162				   sizeof(opt));
163			if (bind(sockfd, (struct sockaddr *)&local_addr,
164				 local_len) < 0)
165				eprintf("bind:");
166		}
167
168		if (connect(sockfd, (struct sockaddr *)&remote_addr,
169			    remote_len) < 0)
170			eprintf("connect:");
171	}
172
173	fds[0].fd = 0;
174	fds[0].events = POLLIN;
175	fds[1].fd = sockfd;
176	fds[1].events = POLLIN;
177
178	while (1) {
179		if (poll(fds, 2, -1) < 0) {
180			if (errno == EINTR)
181				continue;
182			eprintf("poll:");
183		}
184
185		if (fds[0].revents & POLLIN) {
186			n = read(0, buf, sizeof(buf));
187			if (n < 0) {
188				weprintf("read stdin:");
189				break;
190			}
191			if (n == 0) {
192				if (!uflag) {
193					shutdown(sockfd, SHUT_WR);
194					fds[0].fd = -1;
195				} else {
196					break;
197				}
198			} else {
199				if (writeall(sockfd, buf, n) < 0) {
200					weprintf("write socket:");
201					break;
202				}
203			}
204		}
205
206		if (fds[1].revents & POLLIN) {
207			n = read(sockfd, buf, sizeof(buf));
208			if (n < 0) {
209				weprintf("read socket:");
210				break;
211			}
212			if (n == 0) {
213				break;
214			} else {
215				if (writeall(1, buf, n) < 0) {
216					weprintf("write stdout:");
217					break;
218				}
219			}
220		}
221
222		if ((fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) ||
223		    (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))) {
224			break;
225		}
226	}
227
228	close(sockfd);
229	return 0;
230}