OSDN Git Service

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