Справочник по консольным командам Toybox для Android 12


  Ver.0.8.4     Ver.0.8.9     Pending  

Путь: Toys/Pending, команды версии: Ver.4     Ver.9


tcpsvd

Комментарии в файле tcpsvd.c :

usage: tcpsvd [-hEv] [-c N] [-C N[:MSG]] [-b N] [-u User] [-l Name] IP Port Prog

использование: udpsvd [-hEv] [-c N] [-u Пользователь] [-l Имя] Программа IP-порта Создайте сокет TCP/UDP, привяжите его к IP:PORT и прослушивайте входящие соединения. Запустите PROG для каждого соединения. IP IP для прослушивания, 0 = все PORT Порт для прослушивания PROG ARGS Программа для запуска
  • -l ИМЯ Локальное имя хоста (иначе ищет локальное имя хоста в DNS)
  • -u ПОЛЬЗОВАТЕЛЬ[:GRP] Изменение на пользователя/группу после привязки
  • -c N Обрабатывать до N (> 0) подключений одновременно
  • -b N (только TCP) Разрешить отставание примерно в N TCP SYN
  • -C N[ :MSG] (Только TCP) Разрешить только до N (> 0) подключений с одного и того же IP-адреса. Новые соединения с этого IP-адреса закрыты немедленно. MSG записывается узлу перед закрытием
  • -h Ищите имя узла узла
  • -E Не устанавливайте переменные среды
  • -v Подробно

  • usage: tcpsvd [-hEv] [-c N] [-C N[:MSG]] [-b N] [-u User] [-l Name] IP Port Prog

    usage: udpsvd [-hEv] [-c N] [-u User] [-l Name] IP Port Prog

    Create TCP/UDP socket, bind to IP:PORT and listen for incoming connection. Run PROG for each connection. IP IP to listen on, 0 = all PORT Port to listen on PROG ARGS Program to run
  • -l NAME Local hostname (else looks up local hostname in DNS)
  • -u USER[:GRP] Change to user/group after bind
  • -c N Handle up to N (> 0) connections simultaneously
  • -b N (TCP Only) Allow a backlog of approximately N TCP SYNs
  • -C N[:MSG] (TCP Only) Allow only up to N (> 0) connections from the same IP New connections from this IP address are closed immediately. MSG is written to the peer before close
  • -h Look up peer's hostname
  • -E Don't set up environment variables
  • -v Verbose

  • Исходный текст в файле tcpsvd.c

    #define FOR_tcpsvd
    #include "toys.h"
    
    GLOBALS(
      char *name;
      char *user;
      long bn;
      char *nmsg;
      long cn;
    
      int maxc;
      int count_all;
      int udp;
    )
    
    struct list_pid {
      struct list_pid *next;
      char *ip;  
      int pid;
    };
    
    struct list {
      struct list* next;
      char *d;
      int count;
    };
    
    struct hashed {
      struct list *head;
    };
    
    #define HASH_NR 256
    struct hashed h[HASH_NR];
    struct list_pid *pids = NULL;
    
    // convert IP address to string.
    static char *sock_to_address(struct sockaddr *sock, int flags)
    {
      char hbuf[NI_MAXHOST] = {0,};
      char sbuf[NI_MAXSERV] = {0,}; 
      int status = 0;
      socklen_t len = sizeof(struct sockaddr_in6);
    
      if (!(status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf, 
              sizeof(sbuf), flags))) {
        if (flags & NI_NUMERICSERV) return xmprintf("%s:%s",hbuf, sbuf);
        return xmprintf("%s",hbuf);
      }
      error_exit("getnameinfo: %s", gai_strerror(status));
    }
    
    // Insert pid, ip and fd in the list.
    static void insert(struct list_pid **l, int pid, char *addr)
    {
      struct list_pid *newnode = xmalloc(sizeof(struct list_pid));
      newnode->pid = pid;
      newnode->ip = addr;
      newnode->next = NULL;
      if (!*l) *l = newnode;
      else {
        newnode->next = (*l);
       *l = newnode;
      }
    }
    
    // Hashing of IP address.
    static int haship( char *addr)
    {
      uint32_t ip[8] = {0,};
      int count = 0, i = 0;
    
      if (!addr) error_exit("NULL ip");
      while (i < strlen(addr)) {
        while (addr[i] && (addr[i] != ':') && (addr[i] != '.')) {
          ip[count] = ip[count]*10 + (addr[i]-'0');
          i++;
        }
        if (i >= strlen(addr)) break;
        count++;
        i++;
      }
      return (ip[0]^ip[1]^ip[2]^ip[3]^ip[4]^ip[5]^ip[6]^ip[7])%HASH_NR;
    }
    
    // Remove a node from the list.
    static char *delete(struct list_pid **pids, int pid)
    {
      struct list_pid *prev, *free_node, *head = *pids; 
      char *ip = NULL;
     
      if (!head) return NULL;
      prev = free_node = NULL;
      while (head) {
        if (head->pid == pid) {
          ip = head->ip;
          free_node = head;
          if (!prev) *pids = head->next;
          else prev->next = head->next;
          free(free_node);
          return ip;
        }
        prev = head;
        head = head->next;
      }
      return NULL;
    }
    
    // decrement the ref count fora connection, if count reches ZERO then remove the node
    static void remove_connection(char *ip)
    {
      struct list *head, *prev = NULL, *free_node = NULL;
      int hash = haship(ip);
    
      head = h[hash].head;
      while (head) {
        if (!strcmp(ip, head->d)) {
          head->count--;
          free_node = head;
          if (!head->count) {
            if (!prev) h[hash].head = head->next;
            else prev->next = head->next;
            free(free_node);
          }
          break;
        }
        prev = head;
        head = head->next;
      }
      free(ip);
    }
    
    // Handler function.
    static void handle_exit(int sig)
    {
      int status;
      pid_t pid_n = wait(&status);
    
      if (pid_n <= 0) return;
      char *ip = delete(&pids, pid_n);
      if (!ip) return;
      remove_connection(ip);
      TT.count_all--;
      if (toys.optflags & FLAG_v) {
        if (WIFEXITED(status))
          xprintf("%s: end %d exit %d\n",toys.which->name, pid_n, WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
          xprintf("%s: end %d signaled %d\n",toys.which->name, pid_n, WTERMSIG(status));
        if (TT.cn > 1) xprintf("%s: status %d/%d\n",toys.which->name, TT.count_all, TT.cn);
      }
    }
    
    // Grab uid and gid 
    static void get_uidgid(uid_t *uid, gid_t *gid, char *ug)
    {
      struct passwd *pass = NULL;
      struct group *grp = NULL;
      char *user = NULL, *group = NULL;
      unsigned int n;
    
      user = ug;
      group = strchr(ug,':');
      if (group) {
        *group = '\0';
        group++;
      }
      if (!(pass = getpwnam(user))) {
        n = atolx_range(user, 0, INT_MAX);
        if (!(pass = getpwuid(n))) perror_exit("Invalid user '%s'", user);
      }
      *uid = pass->pw_uid;
      *gid = pass->pw_gid;
    
      if (group) {
        if (!(grp = getgrnam(group))) {
          n = atolx_range(group, 0, INT_MAX);
          if (!(grp = getgrgid(n))) perror_exit("Invalid group '%s'",group);
        }    
      }
      if (grp) *gid = grp->gr_gid;
    }
    
    // Bind socket.
    static int create_bind_sock(char *host, struct sockaddr *haddr)
    {
      struct addrinfo hints, *res = NULL, *rp;
      int sockfd, ret, set = 1;
      char *ptr;
      unsigned long port;
    
      errno = 0;
      port = strtoul(toys.optargs[1], &ptr, 10);  
      if (errno || port > 65535) 
        error_exit("Invalid port, Range is [0-65535]");
      if (*ptr) ptr = toys.optargs[1];
      else {
        sprintf(toybuf, "%lu", port);
        ptr = toybuf;
      }
    
      memset(&hints, 0, sizeof hints);
      hints.ai_family = AF_UNSPEC;  
      hints.ai_socktype = ((TT.udp) ?SOCK_DGRAM : SOCK_STREAM);
      if ((ret = getaddrinfo(host, ptr, &hints, &res))) 
        perror_exit("%s", gai_strerror(ret));
    
      for (rp = res; rp; rp = rp->ai_next) 
        if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break;
    
      if (!rp) error_exit("Invalid IP %s", host);
    
      sockfd = xsocket(rp->ai_family, TT.udp ?SOCK_DGRAM :SOCK_STREAM, 0);
      setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
      if (TT.udp) setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &set, sizeof(set));
      xbind(sockfd, rp->ai_addr, rp->ai_addrlen);
      if(haddr) memcpy(haddr, rp->ai_addr, rp->ai_addrlen);
      freeaddrinfo(res);
      return sockfd;
    }
    
    static void handle_signal(int sig)
    {
      if (toys.optflags & FLAG_v) xprintf("got signal %d, exit\n", sig);
      raise(sig);
      _exit(sig + 128); //should not reach here
    } 
    
    void tcpsvd_main(void)
    {
      uid_t uid = 0;
      gid_t gid = 0;
      pid_t pid;
      char haddr[sizeof(struct sockaddr_in6)];
      struct list *head, *newnode;
      int hash, fd, newfd, j;
      char *ptr = NULL, *addr, *server, buf[sizeof(struct sockaddr_in6)];
      socklen_t len = sizeof(buf);
    
      TT.udp = (*toys.which->name == 'u');
      if (TT.udp) toys.optflags &= ~FLAG_C;
      memset(buf, 0, len);
      if (toys.optflags & FLAG_C) {
        if ((ptr = strchr(TT.nmsg, ':'))) {
          *ptr = '\0';
          ptr++;
        }
        TT.maxc = atolx_range(TT.nmsg, 1, INT_MAX);
      }
      
      fd = create_bind_sock(toys.optargs[0], (struct sockaddr*)&haddr);
      if(toys.optflags & FLAG_u) {
        get_uidgid(&uid, &gid, TT.user);
        setuid(uid);
        setgid(gid);
      }
    
      if (!TT.udp && (listen(fd, TT.bn) < 0)) perror_exit("Listen failed");
      server = sock_to_address((struct sockaddr*)&haddr, NI_NUMERICHOST|NI_NUMERICSERV);
      if (toys.optflags & FLAG_v) {
        if (toys.optflags & FLAG_u)
          xprintf("%s: listening on %s, starting, uid %u, gid %u\n"
              ,toys.which->name, server, uid, gid);
        else 
          xprintf("%s: listening on %s, starting\n", toys.which->name, server);
      }
      for (j = 0; j < HASH_NR; j++) h[j].head = NULL;
      sigatexit(handle_signal);  
      signal(SIGCHLD, handle_exit);
    
      while (1) {
        if (TT.count_all  < TT.cn) {
          if (TT.udp) {
            if(recvfrom(fd, NULL, 0, MSG_PEEK, (struct sockaddr *)buf, &len) < 0)
              perror_exit("recvfrom");
            newfd = fd;
          } else {
            newfd = accept(fd, (struct sockaddr *)buf, &len);
            if (newfd < 0) perror_exit("Error on accept");
          }
        } else {
          sigset_t ss;
          sigemptyset(&ss);
          sigsuspend(&ss);
          continue;
        }
        TT.count_all++;
        addr = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST);
    
        hash = haship(addr);
        if (toys.optflags & FLAG_C) {
          for (head = h[hash].head; head; head = head->next)
            if (!strcmp(head->d, addr)) break;
    
          if (head && head->count >= TT.maxc) {
            if (ptr) write(newfd, ptr, strlen(ptr)+1);
            close(newfd);
            TT.count_all--;
            continue;
          }
        }
    
        newnode = (struct list*)xzalloc(sizeof(struct list));
        newnode->d = addr;
        for (head = h[hash].head; head; head = head->next) {
          if (!strcmp(addr, head->d)) {
            head->count++;
            free(newnode);
            break;
          }
        }
    
        if (!head) {
          newnode->next = h[hash].head;
          h[hash].head = newnode;
          h[hash].head->count++;
        }
    
        if (!(pid = xfork())) {
          char *serv = NULL, *clie = NULL;
          char *client = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST | NI_NUMERICSERV);
          if (toys.optflags & FLAG_h) { //lookup name
            if (toys.optflags & FLAG_l) serv = xstrdup(TT.name);
            else serv = sock_to_address((struct sockaddr*)&haddr, 0);
            clie = sock_to_address((struct sockaddr*)buf, 0);
          }
    
          if (!(toys.optflags & FLAG_E)) {
            setenv("PROTO", TT.udp ?"UDP" :"TCP", 1);
            setenv("PROTOLOCALADDR", server, 1);
            setenv("PROTOREMOTEADDR", client, 1);
            if (toys.optflags & FLAG_h) {
              setenv("PROTOLOCALHOST", serv, 1);
              setenv("PROTOREMOTEHOST", clie, 1);
            }
            if (!TT.udp) {
              char max_c[32];
              sprintf(max_c, "%d", TT.maxc);
              setenv("TCPCONCURRENCY", max_c, 1); //Not valid for udp
            }
          }
          if (toys.optflags & FLAG_v) {
            xprintf("%s: start %d %s-%s",toys.which->name, getpid(), server, client);
            if (toys.optflags & FLAG_h) xprintf(" (%s-%s)", serv, clie);
            xputc('\n');
            if (TT.cn > 1) 
              xprintf("%s: status %d/%d\n",toys.which->name, TT.count_all, TT.cn);
          }
          free(client);
          if (toys.optflags & FLAG_h) {
            free(serv);
            free(clie);
          }
          if (TT.udp) xconnect(newfd, (struct sockaddr *)buf, sizeof(buf));
    
          close(0);
          close(1);
          dup2(newfd, 0);
          dup2(newfd, 1);
          xexec(toys.optargs+2); //skip IP PORT
        } else {
          insert(&pids, pid, addr);
          xclose(newfd); //close and reopen for next client.
          if (TT.udp) fd = create_bind_sock(toys.optargs[0],
              (struct sockaddr*)&haddr);
        }
      } //while(1)
    }