• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   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 *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 
53 struct list_pid {
54   struct list_pid *next;
55   char *ip;
56   int pid;
57 };
58 
59 struct list {
60   struct list* next;
61   char *d;
62   int count;
63 };
64 
65 struct hashed {
66   struct list *head;
67 };
68 
69 #define HASH_NR 256
70 struct hashed h[HASH_NR];
71 struct list_pid *pids = NULL;
72 
73 // convert IP address to string.
sock_to_address(struct sockaddr * sock,int flags)74 static 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 // Insert pid, ip and fd in the list.
insert(struct list_pid ** l,int pid,char * addr)90 static 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 // Hashing of IP address.
haship(char * addr)104 static 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 // Remove a node from the list.
delete(struct list_pid ** pids,int pid)123 static 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 // decrement the ref count fora connection, if count reches ZERO then remove the node
remove_connection(char * ip)146 static 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 // Handler function.
handle_exit(int sig)170 static 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 // Grab uid and gid
get_uidgid(uid_t * uid,gid_t * gid,char * ug)190 static 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 // Bind socket.
create_bind_sock(char * host,struct sockaddr * haddr)220 static 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   xbind(sockfd, rp->ai_addr, rp->ai_addrlen);
252   if(haddr) memcpy(haddr, rp->ai_addr, rp->ai_addrlen);
253   freeaddrinfo(res);
254   return sockfd;
255 }
256 
handle_signal(int sig)257 static 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); //should not reach here
262 }
263 
tcpsvd_main(void)264 void 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) { //lookup name
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); //Not valid for udp
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) xconnect(newfd, (struct sockaddr *)buf, sizeof(buf));
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 }
404