xref: /aosp_15_r20/external/toybox/toys/pending/tcpsvd.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* tcpsvd.c - TCP(UDP)/IP service daemon
2  *
3  * Copyright 2013 Ashwini Kumar <[email protected]>
4  * Copyright 2013 Sandeep Sharma <[email protected]>
5  * Copyright 2013 Kyungwan Han <[email protected]>
6  *
7  * No Standard.
8 
9 USE_TCPSVD(NEWTOY(tcpsvd, "^<3c#=30<1b#=20<0C:u:l:hEv", TOYFLAG_USR|TOYFLAG_BIN))
10 USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
11 
12 config TCPSVD
13   bool "tcpsvd"
14   default n
15   depends on TOYBOX_FORK
16   help
17     usage: tcpsvd [-hEv] [-c N] [-C N[:MSG]] [-b N] [-u User] [-l Name] IP Port Prog
18     usage: udpsvd [-hEv] [-c N] [-u User] [-l Name] IP Port Prog
19 
20     Create TCP/UDP socket, bind to IP:PORT and listen for incoming connection.
21     Run PROG for each connection.
22 
23     IP            IP to listen on, 0 = all
24     PORT          Port to listen on
25     PROG ARGS     Program to run
26     -l NAME       Local hostname (else looks up local hostname in DNS)
27     -u USER[:GRP] Change to user/group after bind
28     -c N          Handle up to N (> 0) connections simultaneously
29     -b N          (TCP Only) Allow a backlog of approximately N TCP SYNs
30     -C N[:MSG]    (TCP Only) Allow only up to N (> 0) connections from the same IP
31                   New connections from this IP address are closed
32                   immediately. MSG is written to the peer before close
33     -h            Look up peer's hostname
34     -E            Don't set up environment variables
35     -v            Verbose
36 */
37 
38 #define FOR_tcpsvd
39 #include "toys.h"
40 
41 GLOBALS(
42   char *l, *u, *C;
43   long b, c;
44 
45   int maxc;
46   int count_all;
47   int udp;
48 )
49 
50 struct list_pid {
51   struct list_pid *next;
52   char *ip;
53   int pid;
54 };
55 
56 struct list {
57   struct list* next;
58   char *d;
59   int count;
60 };
61 
62 struct hashed {
63   struct list *head;
64 };
65 
66 #define HASH_NR 256
67 struct hashed h[HASH_NR];
68 struct list_pid *pids = NULL;
69 
70 // convert IP address to string.
sock_to_address(struct sockaddr * sock,int flags)71 static char *sock_to_address(struct sockaddr *sock, int flags)
72 {
73   char hbuf[NI_MAXHOST] = {0,};
74   char sbuf[NI_MAXSERV] = {0,};
75   int status = 0;
76   socklen_t len = sizeof(struct sockaddr_in6);
77 
78   if (!(status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf,
79           sizeof(sbuf), flags))) {
80     if (flags & NI_NUMERICSERV) return xmprintf("%s:%s",hbuf, sbuf);
81     return xmprintf("%s",hbuf);
82   }
83   error_exit("getnameinfo: %s", gai_strerror(status));
84 }
85 
86 // Insert pid, ip and fd in the list.
insert(struct list_pid ** l,int pid,char * addr)87 static void insert(struct list_pid **l, int pid, char *addr)
88 {
89   struct list_pid *newnode = xmalloc(sizeof(struct list_pid));
90   newnode->pid = pid;
91   newnode->ip = addr;
92   newnode->next = NULL;
93   if (!*l) *l = newnode;
94   else {
95     newnode->next = (*l);
96    *l = newnode;
97   }
98 }
99 
100 // Hashing of IP address.
haship(char * addr)101 static int haship(char *addr)
102 {
103   uint32_t ip[8] = {0,};
104   int count = 0, i = 0;
105 
106   if (!addr) error_exit("NULL ip");
107   while (i < strlen(addr)) {
108     while (addr[i] && (addr[i] != ':') && (addr[i] != '.')) {
109       ip[count] = ip[count]*10 + (addr[i]-'0');
110       i++;
111     }
112     if (i >= strlen(addr)) break;
113     count++;
114     i++;
115   }
116   return (ip[0]^ip[1]^ip[2]^ip[3]^ip[4]^ip[5]^ip[6]^ip[7])%HASH_NR;
117 }
118 
119 // Remove a node from the list.
delete(struct list_pid ** pids,int pid)120 static char *delete(struct list_pid **pids, int pid)
121 {
122   struct list_pid *prev, *free_node, *head = *pids;
123   char *ip = NULL;
124 
125   if (!head) return NULL;
126   prev = free_node = NULL;
127   while (head) {
128     if (head->pid == pid) {
129       ip = head->ip;
130       free_node = head;
131       if (!prev) *pids = head->next;
132       else prev->next = head->next;
133       free(free_node);
134       return ip;
135     }
136     prev = head;
137     head = head->next;
138   }
139   return NULL;
140 }
141 
142 // decrement the ref count fora connection, if count reches ZERO then remove the node
remove_connection(char * ip)143 static void remove_connection(char *ip)
144 {
145   struct list *head, *prev = NULL, *free_node = NULL;
146   int hash = haship(ip);
147 
148   head = h[hash].head;
149   while (head) {
150     if (!strcmp(ip, head->d)) {
151       head->count--;
152       free_node = head;
153       if (!head->count) {
154         if (!prev) h[hash].head = head->next;
155         else prev->next = head->next;
156         free(free_node);
157       }
158       break;
159     }
160     prev = head;
161     head = head->next;
162   }
163   free(ip);
164 }
165 
166 // Handler function.
handle_exit(int sig)167 static void handle_exit(int sig)
168 {
169   int status;
170   pid_t pid_n = wait(&status);
171   char *ip = (pid_n<1) ? 0 : delete(&pids, pid_n);
172 
173   if (!ip) return;
174   remove_connection(ip);
175   TT.count_all--;
176   if (FLAG(v)) {
177     if (WIFEXITED(status))
178       xprintf("%s: end %d exit %d\n",toys.which->name, pid_n, WEXITSTATUS(status));
179     else if (WIFSIGNALED(status))
180       xprintf("%s: end %d signaled %d\n",toys.which->name, pid_n, WTERMSIG(status));
181     if (TT.c > 1) xprintf("%s: status %d/%ld\n",toys.which->name, TT.count_all, TT.c);
182   }
183 }
184 
185 // Grab uid and gid
get_uidgid(uid_t * uid,gid_t * gid,char * ug)186 static void get_uidgid(uid_t *uid, gid_t *gid, char *ug)
187 {
188   struct passwd *pass = NULL;
189   struct group *grp = NULL;
190   char *user = NULL, *group = NULL;
191   unsigned int n;
192 
193   user = ug;
194   group = strchr(ug,':');
195   if (group) {
196     *group = '\0';
197     group++;
198   }
199   if (!(pass = getpwnam(user))) {
200     n = atolx_range(user, 0, INT_MAX);
201     if (!(pass = getpwuid(n))) perror_exit("Invalid user '%s'", user);
202   }
203   *uid = pass->pw_uid;
204   *gid = pass->pw_gid;
205 
206   if (group) {
207     if (!(grp = getgrnam(group))) {
208       n = atolx_range(group, 0, INT_MAX);
209       if (!(grp = getgrgid(n))) perror_exit("Invalid group '%s'",group);
210     }
211   }
212   if (grp) *gid = grp->gr_gid;
213 }
214 
215 // Bind socket.
create_bind_sock(char * host,struct sockaddr * haddr)216 static int create_bind_sock(char *host, struct sockaddr *haddr)
217 {
218   struct addrinfo hints, *res = NULL, *rp;
219   int sockfd, ret, set = 1;
220   char *ptr;
221   unsigned long port;
222 
223   errno = 0;
224   port = strtoul(toys.optargs[1], &ptr, 10);
225   if (errno || port > 65535)
226     error_exit("Invalid port, Range is [0-65535]");
227   if (*ptr) ptr = toys.optargs[1];
228   else {
229     sprintf(toybuf, "%lu", port);
230     ptr = toybuf;
231   }
232 
233   memset(&hints, 0, sizeof hints);
234   hints.ai_family = AF_UNSPEC;
235   hints.ai_socktype = (TT.udp ? SOCK_DGRAM : SOCK_STREAM);
236   if ((ret = getaddrinfo(host, ptr, &hints, &res)))
237     perror_exit("%s", gai_strerror(ret));
238 
239   for (rp = res; rp; rp = rp->ai_next)
240     if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break;
241 
242   if (!rp) error_exit("Invalid IP %s", host);
243 
244   sockfd = xsocket(rp->ai_family, TT.udp ? SOCK_DGRAM : SOCK_STREAM, 0);
245   setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
246   if (TT.udp) setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &set, sizeof(set));
247   xbind(sockfd, rp->ai_addr, rp->ai_addrlen);
248   if(haddr) memcpy(haddr, rp->ai_addr, rp->ai_addrlen);
249   freeaddrinfo(res);
250   return sockfd;
251 }
252 
handle_signal(int sig)253 static void handle_signal(int sig)
254 {
255   if (FLAG(v)) xprintf("got signal %d, exit\n", sig);
256   raise(sig);
257   _exit(sig + 128); //should not reach here
258 }
259 
tcpsvd_main(void)260 void tcpsvd_main(void)
261 {
262   uid_t uid = 0;
263   gid_t gid = 0;
264   pid_t pid;
265   char haddr[sizeof(struct sockaddr_in6)];
266   struct list *head, *newnode;
267   int hash, fd, newfd, j;
268   char *ptr = NULL, *addr, *server, buf[sizeof(struct sockaddr_in6)];
269   socklen_t len = sizeof(buf);
270 
271   TT.udp = *toys.which->name == 'u';
272   if (TT.udp) toys.optflags &= ~FLAG_C;
273   memset(buf, 0, len);
274   if (FLAG(C)) {
275     if ((ptr = strchr(TT.C, ':'))) *ptr++ = 0;
276     TT.maxc = atolx_range(TT.C, 1, INT_MAX);
277   }
278 
279   fd = create_bind_sock(toys.optargs[0], (struct sockaddr*)&haddr);
280   if (FLAG(u)) {
281     get_uidgid(&uid, &gid, TT.u);
282     setuid(uid);
283     setgid(gid);
284   }
285 
286   if (!TT.udp && (listen(fd, TT.b) < 0)) perror_exit("Listen failed");
287   server = sock_to_address((struct sockaddr*)&haddr, NI_NUMERICHOST|NI_NUMERICSERV);
288   if (FLAG(v)) {
289     if (FLAG(u))
290       xprintf("%s: listening on %s, starting, uid %u, gid %u\n",
291         toys.which->name, server, uid, gid);
292     else
293       xprintf("%s: listening on %s, starting\n", toys.which->name, server);
294   }
295   for (j = 0; j < HASH_NR; j++) h[j].head = 0;
296   sigatexit(handle_signal);
297   signal(SIGCHLD, handle_exit);
298 
299   while (1) {
300     if (TT.count_all < TT.c) {
301       if (TT.udp) {
302         if (recvfrom(fd, 0, 0, MSG_PEEK, (void *)buf, &len) < 0)
303           perror_exit("recvfrom");
304         newfd = fd;
305       } else {
306         newfd = accept(fd, (struct sockaddr *)buf, &len);
307         if (newfd < 0) perror_exit("Error on accept");
308       }
309     } else {
310       sigset_t ss;
311       sigemptyset(&ss);
312       sigsuspend(&ss);
313       continue;
314     }
315     TT.count_all++;
316     addr = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST);
317 
318     hash = haship(addr);
319     if (FLAG(C)) {
320       for (head = h[hash].head; head; head = head->next)
321         if (!strcmp(head->d, addr)) break;
322 
323       if (head && head->count >= TT.maxc) {
324         if (ptr) write(newfd, ptr, strlen(ptr)); // TODO: this can block
325         close(newfd);
326         TT.count_all--;
327         continue;
328       }
329     }
330 
331     newnode = (struct list*)xzalloc(sizeof(struct list));
332     newnode->d = addr;
333     for (head = h[hash].head; head; head = head->next) {
334       if (!strcmp(addr, head->d)) {
335         head->count++;
336         free(newnode);
337         break;
338       }
339     }
340 
341     if (!head) {
342       newnode->next = h[hash].head;
343       h[hash].head = newnode;
344       h[hash].head->count++;
345     }
346 
347     if (!(pid = xfork())) {
348       char *serv = NULL, *clie = NULL;
349       char *client = sock_to_address((void *)buf, NI_NUMERICHOST | NI_NUMERICSERV);
350       if (FLAG(h)) { //lookup name
351         serv = TT.l ? xstrdup(TT.l) : sock_to_address((void *)&haddr, 0);
352         clie = sock_to_address((void *)buf, 0);
353       }
354 
355       if (!FLAG(E)) {
356         setenv("PROTO", TT.udp ? "UDP" :"TCP", 1);
357         setenv("PROTOLOCALADDR", server, 1);
358         setenv("PROTOREMOTEADDR", client, 1);
359         if (FLAG(h)) {
360           setenv("PROTOLOCALHOST", serv, 1);
361           setenv("PROTOREMOTEHOST", clie, 1);
362         }
363         if (!TT.udp) {
364           char max_c[32];
365           sprintf(max_c, "%d", TT.maxc);
366           setenv("TCPCONCURRENCY", max_c, 1); //Not valid for udp
367         }
368       }
369       if (FLAG(v)) {
370         xprintf("%s: start %d %s-%s",toys.which->name, getpid(), server, client);
371         if (FLAG(h)) xprintf(" (%s-%s)", serv, clie);
372         xputc('\n');
373         if (TT.c > 1)
374           xprintf("%s: status %d/%ld\n",toys.which->name, TT.count_all, TT.c);
375       }
376       free(client);
377       if (FLAG(h)) {
378         free(serv);
379         free(clie);
380       }
381       if (TT.udp) xconnect(newfd, (struct sockaddr *)buf, sizeof(buf));
382 
383       close(0);
384       close(1);
385       dup2(newfd, 0);
386       dup2(newfd, 1);
387       xexec(toys.optargs+2); //skip IP PORT
388     } else {
389       insert(&pids, pid, addr);
390       xclose(newfd); //close and reopen for next client.
391       if (TT.udp) fd = create_bind_sock(toys.optargs[0],
392           (struct sockaddr*)&haddr);
393     }
394   } //while(1)
395 }
396