10f66f451Sopenharmony_ci/* tcpsvd.c - TCP(UDP)/IP service daemon
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
40f66f451Sopenharmony_ci * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
50f66f451Sopenharmony_ci * Copyright 2013 Kyungwan Han <asura321@gmail.com>
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ci * No Standard.
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciUSE_TCPSVD(NEWTOY(tcpsvd, "^<3c#=30<1C:b#=20<0u:l:hEv", TOYFLAG_USR|TOYFLAG_BIN))
100f66f451Sopenharmony_ciUSE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
110f66f451Sopenharmony_ci
120f66f451Sopenharmony_ciconfig TCPSVD
130f66f451Sopenharmony_ci  bool "tcpsvd"
140f66f451Sopenharmony_ci  default n
150f66f451Sopenharmony_ci  depends on TOYBOX_FORK
160f66f451Sopenharmony_ci  help
170f66f451Sopenharmony_ci    usage: tcpsvd [-hEv] [-c N] [-C N[:MSG]] [-b N] [-u User] [-l Name] IP Port Prog
180f66f451Sopenharmony_ci    usage: udpsvd [-hEv] [-c N] [-u User] [-l Name] IP Port Prog
190f66f451Sopenharmony_ci
200f66f451Sopenharmony_ci    Create TCP/UDP socket, bind to IP:PORT and listen for incoming connection.
210f66f451Sopenharmony_ci    Run PROG for each connection.
220f66f451Sopenharmony_ci
230f66f451Sopenharmony_ci    IP            IP to listen on, 0 = all
240f66f451Sopenharmony_ci    PORT          Port to listen on
250f66f451Sopenharmony_ci    PROG ARGS     Program to run
260f66f451Sopenharmony_ci    -l NAME       Local hostname (else looks up local hostname in DNS)
270f66f451Sopenharmony_ci    -u USER[:GRP] Change to user/group after bind
280f66f451Sopenharmony_ci    -c N          Handle up to N (> 0) connections simultaneously
290f66f451Sopenharmony_ci    -b N          (TCP Only) Allow a backlog of approximately N TCP SYNs
300f66f451Sopenharmony_ci    -C N[:MSG]    (TCP Only) Allow only up to N (> 0) connections from the same IP
310f66f451Sopenharmony_ci                  New connections from this IP address are closed
320f66f451Sopenharmony_ci                  immediately. MSG is written to the peer before close
330f66f451Sopenharmony_ci    -h            Look up peer's hostname
340f66f451Sopenharmony_ci    -E            Don't set up environment variables
350f66f451Sopenharmony_ci    -v            Verbose
360f66f451Sopenharmony_ci*/
370f66f451Sopenharmony_ci
380f66f451Sopenharmony_ci#define FOR_tcpsvd
390f66f451Sopenharmony_ci#include "toys.h"
400f66f451Sopenharmony_ci
410f66f451Sopenharmony_ciGLOBALS(
420f66f451Sopenharmony_ci  char *name;
430f66f451Sopenharmony_ci  char *user;
440f66f451Sopenharmony_ci  long bn;
450f66f451Sopenharmony_ci  char *nmsg;
460f66f451Sopenharmony_ci  long cn;
470f66f451Sopenharmony_ci
480f66f451Sopenharmony_ci  int maxc;
490f66f451Sopenharmony_ci  int count_all;
500f66f451Sopenharmony_ci  int udp;
510f66f451Sopenharmony_ci)
520f66f451Sopenharmony_ci
530f66f451Sopenharmony_cistruct list_pid {
540f66f451Sopenharmony_ci  struct list_pid *next;
550f66f451Sopenharmony_ci  char *ip;
560f66f451Sopenharmony_ci  int pid;
570f66f451Sopenharmony_ci};
580f66f451Sopenharmony_ci
590f66f451Sopenharmony_cistruct list {
600f66f451Sopenharmony_ci  struct list* next;
610f66f451Sopenharmony_ci  char *d;
620f66f451Sopenharmony_ci  int count;
630f66f451Sopenharmony_ci};
640f66f451Sopenharmony_ci
650f66f451Sopenharmony_cistruct hashed {
660f66f451Sopenharmony_ci  struct list *head;
670f66f451Sopenharmony_ci};
680f66f451Sopenharmony_ci
690f66f451Sopenharmony_ci#define HASH_NR 256
700f66f451Sopenharmony_cistruct hashed h[HASH_NR];
710f66f451Sopenharmony_cistruct list_pid *pids = NULL;
720f66f451Sopenharmony_ci
730f66f451Sopenharmony_ci// convert IP address to string.
740f66f451Sopenharmony_cistatic char *sock_to_address(struct sockaddr *sock, int flags)
750f66f451Sopenharmony_ci{
760f66f451Sopenharmony_ci  char hbuf[NI_MAXHOST] = {0,};
770f66f451Sopenharmony_ci  char sbuf[NI_MAXSERV] = {0,};
780f66f451Sopenharmony_ci  int status = 0;
790f66f451Sopenharmony_ci  socklen_t len = sizeof(struct sockaddr_in6);
800f66f451Sopenharmony_ci
810f66f451Sopenharmony_ci  if (!(status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf,
820f66f451Sopenharmony_ci          sizeof(sbuf), flags))) {
830f66f451Sopenharmony_ci    if (flags & NI_NUMERICSERV) return xmprintf("%s:%s",hbuf, sbuf);
840f66f451Sopenharmony_ci    return xmprintf("%s",hbuf);
850f66f451Sopenharmony_ci  }
860f66f451Sopenharmony_ci  error_exit("getnameinfo: %s", gai_strerror(status));
870f66f451Sopenharmony_ci}
880f66f451Sopenharmony_ci
890f66f451Sopenharmony_ci// Insert pid, ip and fd in the list.
900f66f451Sopenharmony_cistatic void insert(struct list_pid **l, int pid, char *addr)
910f66f451Sopenharmony_ci{
920f66f451Sopenharmony_ci  struct list_pid *newnode = xmalloc(sizeof(struct list_pid));
930f66f451Sopenharmony_ci  newnode->pid = pid;
940f66f451Sopenharmony_ci  newnode->ip = addr;
950f66f451Sopenharmony_ci  newnode->next = NULL;
960f66f451Sopenharmony_ci  if (!*l) *l = newnode;
970f66f451Sopenharmony_ci  else {
980f66f451Sopenharmony_ci    newnode->next = (*l);
990f66f451Sopenharmony_ci   *l = newnode;
1000f66f451Sopenharmony_ci  }
1010f66f451Sopenharmony_ci}
1020f66f451Sopenharmony_ci
1030f66f451Sopenharmony_ci// Hashing of IP address.
1040f66f451Sopenharmony_cistatic int haship( char *addr)
1050f66f451Sopenharmony_ci{
1060f66f451Sopenharmony_ci  uint32_t ip[8] = {0,};
1070f66f451Sopenharmony_ci  int count = 0, i = 0;
1080f66f451Sopenharmony_ci
1090f66f451Sopenharmony_ci  if (!addr) error_exit("NULL ip");
1100f66f451Sopenharmony_ci  while (i < strlen(addr)) {
1110f66f451Sopenharmony_ci    while (addr[i] && (addr[i] != ':') && (addr[i] != '.')) {
1120f66f451Sopenharmony_ci      ip[count] = ip[count]*10 + (addr[i]-'0');
1130f66f451Sopenharmony_ci      i++;
1140f66f451Sopenharmony_ci    }
1150f66f451Sopenharmony_ci    if (i >= strlen(addr)) break;
1160f66f451Sopenharmony_ci    count++;
1170f66f451Sopenharmony_ci    i++;
1180f66f451Sopenharmony_ci  }
1190f66f451Sopenharmony_ci  return (ip[0]^ip[1]^ip[2]^ip[3]^ip[4]^ip[5]^ip[6]^ip[7])%HASH_NR;
1200f66f451Sopenharmony_ci}
1210f66f451Sopenharmony_ci
1220f66f451Sopenharmony_ci// Remove a node from the list.
1230f66f451Sopenharmony_cistatic char *delete(struct list_pid **pids, int pid)
1240f66f451Sopenharmony_ci{
1250f66f451Sopenharmony_ci  struct list_pid *prev, *free_node, *head = *pids;
1260f66f451Sopenharmony_ci  char *ip = NULL;
1270f66f451Sopenharmony_ci
1280f66f451Sopenharmony_ci  if (!head) return NULL;
1290f66f451Sopenharmony_ci  prev = free_node = NULL;
1300f66f451Sopenharmony_ci  while (head) {
1310f66f451Sopenharmony_ci    if (head->pid == pid) {
1320f66f451Sopenharmony_ci      ip = head->ip;
1330f66f451Sopenharmony_ci      free_node = head;
1340f66f451Sopenharmony_ci      if (!prev) *pids = head->next;
1350f66f451Sopenharmony_ci      else prev->next = head->next;
1360f66f451Sopenharmony_ci      free(free_node);
1370f66f451Sopenharmony_ci      return ip;
1380f66f451Sopenharmony_ci    }
1390f66f451Sopenharmony_ci    prev = head;
1400f66f451Sopenharmony_ci    head = head->next;
1410f66f451Sopenharmony_ci  }
1420f66f451Sopenharmony_ci  return NULL;
1430f66f451Sopenharmony_ci}
1440f66f451Sopenharmony_ci
1450f66f451Sopenharmony_ci// decrement the ref count fora connection, if count reches ZERO then remove the node
1460f66f451Sopenharmony_cistatic void remove_connection(char *ip)
1470f66f451Sopenharmony_ci{
1480f66f451Sopenharmony_ci  struct list *head, *prev = NULL, *free_node = NULL;
1490f66f451Sopenharmony_ci  int hash = haship(ip);
1500f66f451Sopenharmony_ci
1510f66f451Sopenharmony_ci  head = h[hash].head;
1520f66f451Sopenharmony_ci  while (head) {
1530f66f451Sopenharmony_ci    if (!strcmp(ip, head->d)) {
1540f66f451Sopenharmony_ci      head->count--;
1550f66f451Sopenharmony_ci      free_node = head;
1560f66f451Sopenharmony_ci      if (!head->count) {
1570f66f451Sopenharmony_ci        if (!prev) h[hash].head = head->next;
1580f66f451Sopenharmony_ci        else prev->next = head->next;
1590f66f451Sopenharmony_ci        free(free_node);
1600f66f451Sopenharmony_ci      }
1610f66f451Sopenharmony_ci      break;
1620f66f451Sopenharmony_ci    }
1630f66f451Sopenharmony_ci    prev = head;
1640f66f451Sopenharmony_ci    head = head->next;
1650f66f451Sopenharmony_ci  }
1660f66f451Sopenharmony_ci  free(ip);
1670f66f451Sopenharmony_ci}
1680f66f451Sopenharmony_ci
1690f66f451Sopenharmony_ci// Handler function.
1700f66f451Sopenharmony_cistatic void handle_exit(int sig)
1710f66f451Sopenharmony_ci{
1720f66f451Sopenharmony_ci  int status;
1730f66f451Sopenharmony_ci  pid_t pid_n = wait(&status);
1740f66f451Sopenharmony_ci
1750f66f451Sopenharmony_ci  if (pid_n <= 0) return;
1760f66f451Sopenharmony_ci  char *ip = delete(&pids, pid_n);
1770f66f451Sopenharmony_ci  if (!ip) return;
1780f66f451Sopenharmony_ci  remove_connection(ip);
1790f66f451Sopenharmony_ci  TT.count_all--;
1800f66f451Sopenharmony_ci  if (toys.optflags & FLAG_v) {
1810f66f451Sopenharmony_ci    if (WIFEXITED(status))
1820f66f451Sopenharmony_ci      xprintf("%s: end %d exit %d\n",toys.which->name, pid_n, WEXITSTATUS(status));
1830f66f451Sopenharmony_ci    else if (WIFSIGNALED(status))
1840f66f451Sopenharmony_ci      xprintf("%s: end %d signaled %d\n",toys.which->name, pid_n, WTERMSIG(status));
1850f66f451Sopenharmony_ci    if (TT.cn > 1) xprintf("%s: status %d/%d\n",toys.which->name, TT.count_all, TT.cn);
1860f66f451Sopenharmony_ci  }
1870f66f451Sopenharmony_ci}
1880f66f451Sopenharmony_ci
1890f66f451Sopenharmony_ci// Grab uid and gid
1900f66f451Sopenharmony_cistatic void get_uidgid(uid_t *uid, gid_t *gid, char *ug)
1910f66f451Sopenharmony_ci{
1920f66f451Sopenharmony_ci  struct passwd *pass = NULL;
1930f66f451Sopenharmony_ci  struct group *grp = NULL;
1940f66f451Sopenharmony_ci  char *user = NULL, *group = NULL;
1950f66f451Sopenharmony_ci  unsigned int n;
1960f66f451Sopenharmony_ci
1970f66f451Sopenharmony_ci  user = ug;
1980f66f451Sopenharmony_ci  group = strchr(ug,':');
1990f66f451Sopenharmony_ci  if (group) {
2000f66f451Sopenharmony_ci    *group = '\0';
2010f66f451Sopenharmony_ci    group++;
2020f66f451Sopenharmony_ci  }
2030f66f451Sopenharmony_ci  if (!(pass = getpwnam(user))) {
2040f66f451Sopenharmony_ci    n = atolx_range(user, 0, INT_MAX);
2050f66f451Sopenharmony_ci    if (!(pass = getpwuid(n))) perror_exit("Invalid user '%s'", user);
2060f66f451Sopenharmony_ci  }
2070f66f451Sopenharmony_ci  *uid = pass->pw_uid;
2080f66f451Sopenharmony_ci  *gid = pass->pw_gid;
2090f66f451Sopenharmony_ci
2100f66f451Sopenharmony_ci  if (group) {
2110f66f451Sopenharmony_ci    if (!(grp = getgrnam(group))) {
2120f66f451Sopenharmony_ci      n = atolx_range(group, 0, INT_MAX);
2130f66f451Sopenharmony_ci      if (!(grp = getgrgid(n))) perror_exit("Invalid group '%s'",group);
2140f66f451Sopenharmony_ci    }
2150f66f451Sopenharmony_ci  }
2160f66f451Sopenharmony_ci  if (grp) *gid = grp->gr_gid;
2170f66f451Sopenharmony_ci}
2180f66f451Sopenharmony_ci
2190f66f451Sopenharmony_ci// Bind socket.
2200f66f451Sopenharmony_cistatic int create_bind_sock(char *host, struct sockaddr *haddr)
2210f66f451Sopenharmony_ci{
2220f66f451Sopenharmony_ci  struct addrinfo hints, *res = NULL, *rp;
2230f66f451Sopenharmony_ci  int sockfd, ret, set = 1;
2240f66f451Sopenharmony_ci  char *ptr;
2250f66f451Sopenharmony_ci  unsigned long port;
2260f66f451Sopenharmony_ci
2270f66f451Sopenharmony_ci  errno = 0;
2280f66f451Sopenharmony_ci  port = strtoul(toys.optargs[1], &ptr, 10);
2290f66f451Sopenharmony_ci  if (errno || port > 65535)
2300f66f451Sopenharmony_ci    error_exit("Invalid port, Range is [0-65535]");
2310f66f451Sopenharmony_ci  if (*ptr) ptr = toys.optargs[1];
2320f66f451Sopenharmony_ci  else {
2330f66f451Sopenharmony_ci    sprintf(toybuf, "%lu", port);
2340f66f451Sopenharmony_ci    ptr = toybuf;
2350f66f451Sopenharmony_ci  }
2360f66f451Sopenharmony_ci
2370f66f451Sopenharmony_ci  memset(&hints, 0, sizeof hints);
2380f66f451Sopenharmony_ci  hints.ai_family = AF_UNSPEC;
2390f66f451Sopenharmony_ci  hints.ai_socktype = ((TT.udp) ?SOCK_DGRAM : SOCK_STREAM);
2400f66f451Sopenharmony_ci  if ((ret = getaddrinfo(host, ptr, &hints, &res)))
2410f66f451Sopenharmony_ci    perror_exit("%s", gai_strerror(ret));
2420f66f451Sopenharmony_ci
2430f66f451Sopenharmony_ci  for (rp = res; rp; rp = rp->ai_next)
2440f66f451Sopenharmony_ci    if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break;
2450f66f451Sopenharmony_ci
2460f66f451Sopenharmony_ci  if (!rp) error_exit("Invalid IP %s", host);
2470f66f451Sopenharmony_ci
2480f66f451Sopenharmony_ci  sockfd = xsocket(rp->ai_family, TT.udp ?SOCK_DGRAM :SOCK_STREAM, 0);
2490f66f451Sopenharmony_ci  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
2500f66f451Sopenharmony_ci  if (TT.udp) setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &set, sizeof(set));
2510f66f451Sopenharmony_ci  xbind(sockfd, rp->ai_addr, rp->ai_addrlen);
2520f66f451Sopenharmony_ci  if(haddr) memcpy(haddr, rp->ai_addr, rp->ai_addrlen);
2530f66f451Sopenharmony_ci  freeaddrinfo(res);
2540f66f451Sopenharmony_ci  return sockfd;
2550f66f451Sopenharmony_ci}
2560f66f451Sopenharmony_ci
2570f66f451Sopenharmony_cistatic void handle_signal(int sig)
2580f66f451Sopenharmony_ci{
2590f66f451Sopenharmony_ci  if (toys.optflags & FLAG_v) xprintf("got signal %d, exit\n", sig);
2600f66f451Sopenharmony_ci  raise(sig);
2610f66f451Sopenharmony_ci  _exit(sig + 128); //should not reach here
2620f66f451Sopenharmony_ci}
2630f66f451Sopenharmony_ci
2640f66f451Sopenharmony_civoid tcpsvd_main(void)
2650f66f451Sopenharmony_ci{
2660f66f451Sopenharmony_ci  uid_t uid = 0;
2670f66f451Sopenharmony_ci  gid_t gid = 0;
2680f66f451Sopenharmony_ci  pid_t pid;
2690f66f451Sopenharmony_ci  char haddr[sizeof(struct sockaddr_in6)];
2700f66f451Sopenharmony_ci  struct list *head, *newnode;
2710f66f451Sopenharmony_ci  int hash, fd, newfd, j;
2720f66f451Sopenharmony_ci  char *ptr = NULL, *addr, *server, buf[sizeof(struct sockaddr_in6)];
2730f66f451Sopenharmony_ci  socklen_t len = sizeof(buf);
2740f66f451Sopenharmony_ci
2750f66f451Sopenharmony_ci  TT.udp = (*toys.which->name == 'u');
2760f66f451Sopenharmony_ci  if (TT.udp) toys.optflags &= ~FLAG_C;
2770f66f451Sopenharmony_ci  memset(buf, 0, len);
2780f66f451Sopenharmony_ci  if (toys.optflags & FLAG_C) {
2790f66f451Sopenharmony_ci    if ((ptr = strchr(TT.nmsg, ':'))) {
2800f66f451Sopenharmony_ci      *ptr = '\0';
2810f66f451Sopenharmony_ci      ptr++;
2820f66f451Sopenharmony_ci    }
2830f66f451Sopenharmony_ci    TT.maxc = atolx_range(TT.nmsg, 1, INT_MAX);
2840f66f451Sopenharmony_ci  }
2850f66f451Sopenharmony_ci
2860f66f451Sopenharmony_ci  fd = create_bind_sock(toys.optargs[0], (struct sockaddr*)&haddr);
2870f66f451Sopenharmony_ci  if(toys.optflags & FLAG_u) {
2880f66f451Sopenharmony_ci    get_uidgid(&uid, &gid, TT.user);
2890f66f451Sopenharmony_ci    setuid(uid);
2900f66f451Sopenharmony_ci    setgid(gid);
2910f66f451Sopenharmony_ci  }
2920f66f451Sopenharmony_ci
2930f66f451Sopenharmony_ci  if (!TT.udp && (listen(fd, TT.bn) < 0)) perror_exit("Listen failed");
2940f66f451Sopenharmony_ci  server = sock_to_address((struct sockaddr*)&haddr, NI_NUMERICHOST|NI_NUMERICSERV);
2950f66f451Sopenharmony_ci  if (toys.optflags & FLAG_v) {
2960f66f451Sopenharmony_ci    if (toys.optflags & FLAG_u)
2970f66f451Sopenharmony_ci      xprintf("%s: listening on %s, starting, uid %u, gid %u\n"
2980f66f451Sopenharmony_ci          ,toys.which->name, server, uid, gid);
2990f66f451Sopenharmony_ci    else
3000f66f451Sopenharmony_ci      xprintf("%s: listening on %s, starting\n", toys.which->name, server);
3010f66f451Sopenharmony_ci  }
3020f66f451Sopenharmony_ci  for (j = 0; j < HASH_NR; j++) h[j].head = NULL;
3030f66f451Sopenharmony_ci  sigatexit(handle_signal);
3040f66f451Sopenharmony_ci  signal(SIGCHLD, handle_exit);
3050f66f451Sopenharmony_ci
3060f66f451Sopenharmony_ci  while (1) {
3070f66f451Sopenharmony_ci    if (TT.count_all  < TT.cn) {
3080f66f451Sopenharmony_ci      if (TT.udp) {
3090f66f451Sopenharmony_ci        if(recvfrom(fd, NULL, 0, MSG_PEEK, (struct sockaddr *)buf, &len) < 0)
3100f66f451Sopenharmony_ci          perror_exit("recvfrom");
3110f66f451Sopenharmony_ci        newfd = fd;
3120f66f451Sopenharmony_ci      } else {
3130f66f451Sopenharmony_ci        newfd = accept(fd, (struct sockaddr *)buf, &len);
3140f66f451Sopenharmony_ci        if (newfd < 0) perror_exit("Error on accept");
3150f66f451Sopenharmony_ci      }
3160f66f451Sopenharmony_ci    } else {
3170f66f451Sopenharmony_ci      sigset_t ss;
3180f66f451Sopenharmony_ci      sigemptyset(&ss);
3190f66f451Sopenharmony_ci      sigsuspend(&ss);
3200f66f451Sopenharmony_ci      continue;
3210f66f451Sopenharmony_ci    }
3220f66f451Sopenharmony_ci    TT.count_all++;
3230f66f451Sopenharmony_ci    addr = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST);
3240f66f451Sopenharmony_ci
3250f66f451Sopenharmony_ci    hash = haship(addr);
3260f66f451Sopenharmony_ci    if (toys.optflags & FLAG_C) {
3270f66f451Sopenharmony_ci      for (head = h[hash].head; head; head = head->next)
3280f66f451Sopenharmony_ci        if (!strcmp(head->d, addr)) break;
3290f66f451Sopenharmony_ci
3300f66f451Sopenharmony_ci      if (head && head->count >= TT.maxc) {
3310f66f451Sopenharmony_ci        if (ptr) write(newfd, ptr, strlen(ptr)+1);
3320f66f451Sopenharmony_ci        close(newfd);
3330f66f451Sopenharmony_ci        TT.count_all--;
3340f66f451Sopenharmony_ci        continue;
3350f66f451Sopenharmony_ci      }
3360f66f451Sopenharmony_ci    }
3370f66f451Sopenharmony_ci
3380f66f451Sopenharmony_ci    newnode = (struct list*)xzalloc(sizeof(struct list));
3390f66f451Sopenharmony_ci    newnode->d = addr;
3400f66f451Sopenharmony_ci    for (head = h[hash].head; head; head = head->next) {
3410f66f451Sopenharmony_ci      if (!strcmp(addr, head->d)) {
3420f66f451Sopenharmony_ci        head->count++;
3430f66f451Sopenharmony_ci        free(newnode);
3440f66f451Sopenharmony_ci        break;
3450f66f451Sopenharmony_ci      }
3460f66f451Sopenharmony_ci    }
3470f66f451Sopenharmony_ci
3480f66f451Sopenharmony_ci    if (!head) {
3490f66f451Sopenharmony_ci      newnode->next = h[hash].head;
3500f66f451Sopenharmony_ci      h[hash].head = newnode;
3510f66f451Sopenharmony_ci      h[hash].head->count++;
3520f66f451Sopenharmony_ci    }
3530f66f451Sopenharmony_ci
3540f66f451Sopenharmony_ci    if (!(pid = xfork())) {
3550f66f451Sopenharmony_ci      char *serv = NULL, *clie = NULL;
3560f66f451Sopenharmony_ci      char *client = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST | NI_NUMERICSERV);
3570f66f451Sopenharmony_ci      if (toys.optflags & FLAG_h) { //lookup name
3580f66f451Sopenharmony_ci        if (toys.optflags & FLAG_l) serv = xstrdup(TT.name);
3590f66f451Sopenharmony_ci        else serv = sock_to_address((struct sockaddr*)&haddr, 0);
3600f66f451Sopenharmony_ci        clie = sock_to_address((struct sockaddr*)buf, 0);
3610f66f451Sopenharmony_ci      }
3620f66f451Sopenharmony_ci
3630f66f451Sopenharmony_ci      if (!(toys.optflags & FLAG_E)) {
3640f66f451Sopenharmony_ci        setenv("PROTO", TT.udp ?"UDP" :"TCP", 1);
3650f66f451Sopenharmony_ci        setenv("PROTOLOCALADDR", server, 1);
3660f66f451Sopenharmony_ci        setenv("PROTOREMOTEADDR", client, 1);
3670f66f451Sopenharmony_ci        if (toys.optflags & FLAG_h) {
3680f66f451Sopenharmony_ci          setenv("PROTOLOCALHOST", serv, 1);
3690f66f451Sopenharmony_ci          setenv("PROTOREMOTEHOST", clie, 1);
3700f66f451Sopenharmony_ci        }
3710f66f451Sopenharmony_ci        if (!TT.udp) {
3720f66f451Sopenharmony_ci          char max_c[32];
3730f66f451Sopenharmony_ci          sprintf(max_c, "%d", TT.maxc);
3740f66f451Sopenharmony_ci          setenv("TCPCONCURRENCY", max_c, 1); //Not valid for udp
3750f66f451Sopenharmony_ci        }
3760f66f451Sopenharmony_ci      }
3770f66f451Sopenharmony_ci      if (toys.optflags & FLAG_v) {
3780f66f451Sopenharmony_ci        xprintf("%s: start %d %s-%s",toys.which->name, getpid(), server, client);
3790f66f451Sopenharmony_ci        if (toys.optflags & FLAG_h) xprintf(" (%s-%s)", serv, clie);
3800f66f451Sopenharmony_ci        xputc('\n');
3810f66f451Sopenharmony_ci        if (TT.cn > 1)
3820f66f451Sopenharmony_ci          xprintf("%s: status %d/%d\n",toys.which->name, TT.count_all, TT.cn);
3830f66f451Sopenharmony_ci      }
3840f66f451Sopenharmony_ci      free(client);
3850f66f451Sopenharmony_ci      if (toys.optflags & FLAG_h) {
3860f66f451Sopenharmony_ci        free(serv);
3870f66f451Sopenharmony_ci        free(clie);
3880f66f451Sopenharmony_ci      }
3890f66f451Sopenharmony_ci      if (TT.udp) xconnect(newfd, (struct sockaddr *)buf, sizeof(buf));
3900f66f451Sopenharmony_ci
3910f66f451Sopenharmony_ci      close(0);
3920f66f451Sopenharmony_ci      close(1);
3930f66f451Sopenharmony_ci      dup2(newfd, 0);
3940f66f451Sopenharmony_ci      dup2(newfd, 1);
3950f66f451Sopenharmony_ci      xexec(toys.optargs+2); //skip IP PORT
3960f66f451Sopenharmony_ci    } else {
3970f66f451Sopenharmony_ci      insert(&pids, pid, addr);
3980f66f451Sopenharmony_ci      xclose(newfd); //close and reopen for next client.
3990f66f451Sopenharmony_ci      if (TT.udp) fd = create_bind_sock(toys.optargs[0],
4000f66f451Sopenharmony_ci          (struct sockaddr*)&haddr);
4010f66f451Sopenharmony_ci    }
4020f66f451Sopenharmony_ci  } //while(1)
4030f66f451Sopenharmony_ci}
404