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}