1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
#include "toys.h"
int xsocket(int domain, int type, int protocol)
{
int fd = socket(domain, type, protocol);
if (fd < 0) perror_exit("socket %x %x", type, protocol);
fcntl(fd, F_SETFD, FD_CLOEXEC);
return fd;
}
void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len)
{
if (-1 == setsockopt(fd, level, opt, val, len)) perror_exit("setsockopt");
}
struct addrinfo *xgetaddrinfo(char *host, char *port, int family, int socktype,
int protocol, int flags)
{
struct addrinfo info, *ai;
int rc;
memset(&info, 0, sizeof(struct addrinfo));
info.ai_family = family;
info.ai_socktype = socktype;
info.ai_protocol = protocol;
info.ai_flags = flags;
rc = getaddrinfo(host, port, &info, &ai);
if (rc || !ai)
error_exit("%s%s%s: %s", host, port ? ":" : "", port ? port : "",
rc ? gai_strerror(rc) : "not found");
return ai;
}
int xconnect(struct addrinfo *ai_arg)
{
struct addrinfo *ai;
int fd = -1;
// Try all the returned addresses. Report errors if last entry can't connect.
for (ai = ai_arg; ai; ai = ai->ai_next) {
fd = (ai->ai_next ? socket : xsocket)(ai->ai_family, ai->ai_socktype,
ai->ai_protocol);
if (!connect(fd, ai->ai_addr, ai->ai_addrlen)) break;
else if (!ai->ai_next) perror_exit("connect");
close(fd);
}
freeaddrinfo(ai_arg);
return fd;
}
int xbind(struct addrinfo *ai_arg)
{
struct addrinfo *ai;
int fd = -1;
// Try all the returned addresses. Report errors if last entry can't connect.
for (ai = ai_arg; ai; ai = ai->ai_next) {
fd = (ai->ai_next ? socket : xsocket)(ai->ai_family, ai->ai_socktype,
ai->ai_protocol);
if (!bind(fd, ai->ai_addr, ai->ai_addrlen)) break;
else if (!ai->ai_next) perror_exit("connect");
close(fd);
}
freeaddrinfo(ai_arg);
return fd;
}
int xpoll(struct pollfd *fds, int nfds, int timeout)
{
int i;
for (;;) {
if (0>(i = poll(fds, nfds, timeout))) {
if (toys.signal) return i;
if (errno != EINTR && errno != ENOMEM) perror_exit("xpoll");
else if (timeout>0) timeout--;
} else return i;
}
}
// Loop forwarding data from in1 to out1 and in2 to out2, handling
// half-connection shutdown. timeouts return if no data for X miliseconds.
// Returns 0: both closed, 1 shutdown_timeout, 2 timeout
int pollinate(int in1, int in2, int out1, int out2, int timeout, int shutdown_timeout)
{
struct pollfd pollfds[2];
int i, pollcount = 2;
memset(pollfds, 0, 2*sizeof(struct pollfd));
pollfds[0].events = pollfds[1].events = POLLIN;
pollfds[0].fd = in1;
pollfds[1].fd = in2;
// Poll loop copying data from each fd to the other one.
for (;;) {
if (!xpoll(pollfds, pollcount, timeout)) return pollcount;
for (i=0; i<pollcount; i++) {
if (pollfds[i].revents & POLLIN) {
int len = read(pollfds[i].fd, libbuf, sizeof(libbuf));
if (len<1) pollfds[i].revents = POLLHUP;
else xwrite(i ? out2 : out1, libbuf, len);
}
if (pollfds[i].revents & POLLHUP) {
// Close half-connection. This is needed for things like
// "echo GET / | netcat landley.net 80"
// Note that in1 closing triggers timeout, in2 returns now.
if (i) {
shutdown(pollfds[0].fd, SHUT_WR);
pollcount--;
timeout = shutdown_timeout;
} else return 0;
}
}
}
}
// Return converted ipv4/ipv6 numeric address in libbuf
char *ntop(struct sockaddr *sa)
{
void *addr;
if (sa->sa_family == AF_INET) addr = &((struct sockaddr_in *)sa)->sin_addr;
else addr = &((struct sockaddr_in6 *)sa)->sin6_addr;
inet_ntop(sa->sa_family, addr, libbuf, sizeof(libbuf));
return libbuf;
}
|