toybox/toys/pending/tcpsvd.c
<<
>>
Prefs
   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
   9USE_TCPSVD(NEWTOY(tcpsvd, "^<3c#=30<1C:b#=20<0u:l:hEv", TOYFLAG_USR|TOYFLAG_BIN))
  10USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
  11
  12config 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
  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// convert IP address to string.
  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// Insert pid, ip and fd in the list.
  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// Hashing of IP address.
 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// Remove a node from the list.
 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// decrement the ref count fora connection, if count reches ZERO then remove the node
 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// Handler function.
 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// Grab uid and gid 
 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// Bind socket.
 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); //should not reach here
 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) { //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 && (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); //skip IP PORT
 397    } else {
 398      insert(&pids, pid, addr);
 399      xclose(newfd); //close and reopen for next client.
 400      if (TT.udp) fd = create_bind_sock(toys.optargs[0],
 401          (struct sockaddr*)&haddr);
 402    }
 403  } //while(1)
 404}
 405