Home | History | Annotate | Download | only in pending
      1 /* tcpsvd.c - TCP(UDP)/IP service daemon
      2  *
      3  * Copyright 2013 Ashwini Kumar <ak.ashwini (at) gmail.com>
      4  * Copyright 2013 Sandeep Sharma <sandeep.jack2756 (at) gmail.com>
      5  * Copyright 2013 Kyungwan Han <asura321 (at) 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 }
    404