Mercurial > hg > toybox
view toys/pending/tcpsvd.c @ 1572:da1bf31ed322 draft
Tweak the "ignoring return value" fortify workaround for readlinkat.
We zero the buffer and if the link read fails that's left alone, so
it's ok for the symlink not to be there. Unfortunately, typecasting the
return value to (void) doesn't shut up gcc, and having an if(); with the
semicolon on the same line doesn't shut up llvm. (The semicolon on a new
line would, but C does not have significant whitespace and I'm not going
to humor llvm if it plans to start.)
So far, empty curly brackets consistently get the warning to shut up.
author | Rob Landley <rob@landley.net> |
---|---|
date | Mon, 24 Nov 2014 17:23:23 -0600 |
parents | 85f297591693 |
children | 5fac2769a159 |
line wrap: on
line source
/* tcpsvd.c - TCP(UDP)/IP service daemon * * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com> * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com> * Copyright 2013 Kyungwan Han <asura321@gmail.com> * * No Standard. USE_TCPSVD(NEWTOY(tcpsvd, "^<3c#=30<1C:b#=20<0u:l:hEv", TOYFLAG_USR|TOYFLAG_BIN)) USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, OPTSTR_tcpsvd, TOYFLAG_USR|TOYFLAG_BIN)) config TCPSVD bool "tcpsvd" default n help 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 */ #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)); if ((bind(sockfd, rp->ai_addr, rp->ai_addrlen)) < 0) perror_exit("Bind failed"); 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 && (connect(newfd, (struct sockaddr *)buf, sizeof(buf)) < 0)) perror_exit("connect"); close(0); close(1); dup2(newfd, 0); dup2(newfd, 1); xexec_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) }