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