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 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 41 GLOBALS( 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 53 struct list_pid { 54 struct list_pid *next; 55 char *ip; 56 int pid; 57 }; 58 59 struct list { 60 struct list* next; 61 char *d; 62 int count; 63 }; 64 65 struct hashed { 66 struct list *head; 67 }; 68 69 #define HASH_NR 256 70 struct hashed h[HASH_NR]; 71 struct list_pid *pids = NULL; 72 73 // convert IP address to string. 74 static 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. 90 static 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. 104 static 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. 123 static 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 146 static 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. 170 static 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 190 static 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. 220 static 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 257 static 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 264 void 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