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#define FOR_tcpsvd
39#include "toys.h"
40
41GLOBALS(
42 char *name;
43 char *user;
44 long bn;
45 char *nmsg;
46 long cn;
47
48 int maxc;
49 int count_all;
50 int udp;
51)
52
53struct list_pid {
54 struct list_pid *next;
55 char *ip;
56 int pid;
57};
58
59struct list {
60 struct list* next;
61 char *d;
62 int count;
63};
64
65struct hashed {
66 struct list *head;
67};
68
69#define HASH_NR 256
70struct hashed h[HASH_NR];
71struct list_pid *pids = NULL;
72
73
74static char *sock_to_address(struct sockaddr *sock, int flags)
75{
76 char hbuf[NI_MAXHOST] = {0,};
77 char sbuf[NI_MAXSERV] = {0,};
78 int status = 0;
79 socklen_t len = sizeof(struct sockaddr_in6);
80
81 if (!(status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf,
82 sizeof(sbuf), flags))) {
83 if (flags & NI_NUMERICSERV) return xmprintf("%s:%s",hbuf, sbuf);
84 return xmprintf("%s",hbuf);
85 }
86 error_exit("getnameinfo: %s", gai_strerror(status));
87}
88
89
90static void insert(struct list_pid **l, int pid, char *addr)
91{
92 struct list_pid *newnode = xmalloc(sizeof(struct list_pid));
93 newnode->pid = pid;
94 newnode->ip = addr;
95 newnode->next = NULL;
96 if (!*l) *l = newnode;
97 else {
98 newnode->next = (*l);
99 *l = newnode;
100 }
101}
102
103
104static int haship( char *addr)
105{
106 uint32_t ip[8] = {0,};
107 int count = 0, i = 0;
108
109 if (!addr) error_exit("NULL ip");
110 while (i < strlen(addr)) {
111 while (addr[i] && (addr[i] != ':') && (addr[i] != '.')) {
112 ip[count] = ip[count]*10 + (addr[i]-'0');
113 i++;
114 }
115 if (i >= strlen(addr)) break;
116 count++;
117 i++;
118 }
119 return (ip[0]^ip[1]^ip[2]^ip[3]^ip[4]^ip[5]^ip[6]^ip[7])%HASH_NR;
120}
121
122
123static char *delete(struct list_pid **pids, int pid)
124{
125 struct list_pid *prev, *free_node, *head = *pids;
126 char *ip = NULL;
127
128 if (!head) return NULL;
129 prev = free_node = NULL;
130 while (head) {
131 if (head->pid == pid) {
132 ip = head->ip;
133 free_node = head;
134 if (!prev) *pids = head->next;
135 else prev->next = head->next;
136 free(free_node);
137 return ip;
138 }
139 prev = head;
140 head = head->next;
141 }
142 return NULL;
143}
144
145
146static void remove_connection(char *ip)
147{
148 struct list *head, *prev = NULL, *free_node = NULL;
149 int hash = haship(ip);
150
151 head = h[hash].head;
152 while (head) {
153 if (!strcmp(ip, head->d)) {
154 head->count--;
155 free_node = head;
156 if (!head->count) {
157 if (!prev) h[hash].head = head->next;
158 else prev->next = head->next;
159 free(free_node);
160 }
161 break;
162 }
163 prev = head;
164 head = head->next;
165 }
166 free(ip);
167}
168
169
170static void handle_exit(int sig)
171{
172 int status;
173 pid_t pid_n = wait(&status);
174
175 if (pid_n <= 0) return;
176 char *ip = delete(&pids, pid_n);
177 if (!ip) return;
178 remove_connection(ip);
179 TT.count_all--;
180 if (toys.optflags & FLAG_v) {
181 if (WIFEXITED(status))
182 xprintf("%s: end %d exit %d\n",toys.which->name, pid_n, WEXITSTATUS(status));
183 else if (WIFSIGNALED(status))
184 xprintf("%s: end %d signaled %d\n",toys.which->name, pid_n, WTERMSIG(status));
185 if (TT.cn > 1) xprintf("%s: status %d/%d\n",toys.which->name, TT.count_all, TT.cn);
186 }
187}
188
189
190static void get_uidgid(uid_t *uid, gid_t *gid, char *ug)
191{
192 struct passwd *pass = NULL;
193 struct group *grp = NULL;
194 char *user = NULL, *group = NULL;
195 unsigned int n;
196
197 user = ug;
198 group = strchr(ug,':');
199 if (group) {
200 *group = '\0';
201 group++;
202 }
203 if (!(pass = getpwnam(user))) {
204 n = atolx_range(user, 0, INT_MAX);
205 if (!(pass = getpwuid(n))) perror_exit("Invalid user '%s'", user);
206 }
207 *uid = pass->pw_uid;
208 *gid = pass->pw_gid;
209
210 if (group) {
211 if (!(grp = getgrnam(group))) {
212 n = atolx_range(group, 0, INT_MAX);
213 if (!(grp = getgrgid(n))) perror_exit("Invalid group '%s'",group);
214 }
215 }
216 if (grp) *gid = grp->gr_gid;
217}
218
219
220static int create_bind_sock(char *host, struct sockaddr *haddr)
221{
222 struct addrinfo hints, *res = NULL, *rp;
223 int sockfd, ret, set = 1;
224 char *ptr;
225 unsigned long port;
226
227 errno = 0;
228 port = strtoul(toys.optargs[1], &ptr, 10);
229 if (errno || port > 65535)
230 error_exit("Invalid port, Range is [0-65535]");
231 if (*ptr) ptr = toys.optargs[1];
232 else {
233 sprintf(toybuf, "%lu", port);
234 ptr = toybuf;
235 }
236
237 memset(&hints, 0, sizeof hints);
238 hints.ai_family = AF_UNSPEC;
239 hints.ai_socktype = ((TT.udp) ?SOCK_DGRAM : SOCK_STREAM);
240 if ((ret = getaddrinfo(host, ptr, &hints, &res)))
241 perror_exit("%s", gai_strerror(ret));
242
243 for (rp = res; rp; rp = rp->ai_next)
244 if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break;
245
246 if (!rp) error_exit("Invalid IP %s", host);
247
248 sockfd = xsocket(rp->ai_family, TT.udp ?SOCK_DGRAM :SOCK_STREAM, 0);
249 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
250 if (TT.udp) setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &set, sizeof(set));
251 if ((bind(sockfd, rp->ai_addr, rp->ai_addrlen)) < 0) perror_exit("Bind failed");
252 if(haddr) memcpy(haddr, rp->ai_addr, rp->ai_addrlen);
253 freeaddrinfo(res);
254 return sockfd;
255}
256
257static void handle_signal(int sig)
258{
259 if (toys.optflags & FLAG_v) xprintf("got signal %d, exit\n", sig);
260 raise(sig);
261 _exit(sig + 128);
262}
263
264void tcpsvd_main(void)
265{
266 uid_t uid = 0;
267 gid_t gid = 0;
268 pid_t pid;
269 char haddr[sizeof(struct sockaddr_in6)];
270 struct list *head, *newnode;
271 int hash, fd, newfd, j;
272 char *ptr = NULL, *addr, *server, buf[sizeof(struct sockaddr_in6)];
273 socklen_t len = sizeof(buf);
274
275 TT.udp = (*toys.which->name == 'u');
276 if (TT.udp) toys.optflags &= ~FLAG_C;
277 memset(buf, 0, len);
278 if (toys.optflags & FLAG_C) {
279 if ((ptr = strchr(TT.nmsg, ':'))) {
280 *ptr = '\0';
281 ptr++;
282 }
283 TT.maxc = atolx_range(TT.nmsg, 1, INT_MAX);
284 }
285
286 fd = create_bind_sock(toys.optargs[0], (struct sockaddr*)&haddr);
287 if(toys.optflags & FLAG_u) {
288 get_uidgid(&uid, &gid, TT.user);
289 setuid(uid);
290 setgid(gid);
291 }
292
293 if (!TT.udp && (listen(fd, TT.bn) < 0)) perror_exit("Listen failed");
294 server = sock_to_address((struct sockaddr*)&haddr, NI_NUMERICHOST|NI_NUMERICSERV);
295 if (toys.optflags & FLAG_v) {
296 if (toys.optflags & FLAG_u)
297 xprintf("%s: listening on %s, starting, uid %u, gid %u\n"
298 ,toys.which->name, server, uid, gid);
299 else
300 xprintf("%s: listening on %s, starting\n", toys.which->name, server);
301 }
302 for (j = 0; j < HASH_NR; j++) h[j].head = NULL;
303 sigatexit(handle_signal);
304 signal(SIGCHLD, handle_exit);
305
306 while (1) {
307 if (TT.count_all < TT.cn) {
308 if (TT.udp) {
309 if(recvfrom(fd, NULL, 0, MSG_PEEK, (struct sockaddr *)buf, &len) < 0)
310 perror_exit("recvfrom");
311 newfd = fd;
312 } else {
313 newfd = accept(fd, (struct sockaddr *)buf, &len);
314 if (newfd < 0) perror_exit("Error on accept");
315 }
316 } else {
317 sigset_t ss;
318 sigemptyset(&ss);
319 sigsuspend(&ss);
320 continue;
321 }
322 TT.count_all++;
323 addr = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST);
324
325 hash = haship(addr);
326 if (toys.optflags & FLAG_C) {
327 for (head = h[hash].head; head; head = head->next)
328 if (!strcmp(head->d, addr)) break;
329
330 if (head && head->count >= TT.maxc) {
331 if (ptr) write(newfd, ptr, strlen(ptr)+1);
332 close(newfd);
333 TT.count_all--;
334 continue;
335 }
336 }
337
338 newnode = (struct list*)xzalloc(sizeof(struct list));
339 newnode->d = addr;
340 for (head = h[hash].head; head; head = head->next) {
341 if (!strcmp(addr, head->d)) {
342 head->count++;
343 free(newnode);
344 break;
345 }
346 }
347
348 if (!head) {
349 newnode->next = h[hash].head;
350 h[hash].head = newnode;
351 h[hash].head->count++;
352 }
353
354 if (!(pid = xfork())) {
355 char *serv = NULL, *clie = NULL;
356 char *client = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST | NI_NUMERICSERV);
357 if (toys.optflags & FLAG_h) {
358 if (toys.optflags & FLAG_l) serv = xstrdup(TT.name);
359 else serv = sock_to_address((struct sockaddr*)&haddr, 0);
360 clie = sock_to_address((struct sockaddr*)buf, 0);
361 }
362
363 if (!(toys.optflags & FLAG_E)) {
364 setenv("PROTO", TT.udp ?"UDP" :"TCP", 1);
365 setenv("PROTOLOCALADDR", server, 1);
366 setenv("PROTOREMOTEADDR", client, 1);
367 if (toys.optflags & FLAG_h) {
368 setenv("PROTOLOCALHOST", serv, 1);
369 setenv("PROTOREMOTEHOST", clie, 1);
370 }
371 if (!TT.udp) {
372 char max_c[32];
373 sprintf(max_c, "%d", TT.maxc);
374 setenv("TCPCONCURRENCY", max_c, 1);
375 }
376 }
377 if (toys.optflags & FLAG_v) {
378 xprintf("%s: start %d %s-%s",toys.which->name, getpid(), server, client);
379 if (toys.optflags & FLAG_h) xprintf(" (%s-%s)", serv, clie);
380 xputc('\n');
381 if (TT.cn > 1)
382 xprintf("%s: status %d/%d\n",toys.which->name, TT.count_all, TT.cn);
383 }
384 free(client);
385 if (toys.optflags & FLAG_h) {
386 free(serv);
387 free(clie);
388 }
389 if (TT.udp && (connect(newfd, (struct sockaddr *)buf, sizeof(buf)) < 0))
390 perror_exit("connect");
391
392 close(0);
393 close(1);
394 dup2(newfd, 0);
395 dup2(newfd, 1);
396 xexec(toys.optargs+2);
397 } else {
398 insert(&pids, pid, addr);
399 xclose(newfd);
400 if (TT.udp) fd = create_bind_sock(toys.optargs[0],
401 (struct sockaddr*)&haddr);
402 }
403 }
404}
405