tcpsvd.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  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. USE_TCPSVD(NEWTOY(tcpsvd, "^<3c#=30<1C:b#=20<0u:l:hEv", TOYFLAG_USR|TOYFLAG_BIN))
  9. USE_TCPSVD(OLDTOY(udpsvd, tcpsvd, TOYFLAG_USR|TOYFLAG_BIN))
  10. config TCPSVD
  11. bool "tcpsvd"
  12. default n
  13. depends on TOYBOX_FORK
  14. help
  15. usage: tcpsvd [-hEv] [-c N] [-C N[:MSG]] [-b N] [-u User] [-l Name] IP Port Prog
  16. usage: udpsvd [-hEv] [-c N] [-u User] [-l Name] IP Port Prog
  17. Create TCP/UDP socket, bind to IP:PORT and listen for incoming connection.
  18. Run PROG for each connection.
  19. IP IP to listen on, 0 = all
  20. PORT Port to listen on
  21. PROG ARGS Program to run
  22. -l NAME Local hostname (else looks up local hostname in DNS)
  23. -u USER[:GRP] Change to user/group after bind
  24. -c N Handle up to N (> 0) connections simultaneously
  25. -b N (TCP Only) Allow a backlog of approximately N TCP SYNs
  26. -C N[:MSG] (TCP Only) Allow only up to N (> 0) connections from the same IP
  27. New connections from this IP address are closed
  28. immediately. MSG is written to the peer before close
  29. -h Look up peer's hostname
  30. -E Don't set up environment variables
  31. -v Verbose
  32. */
  33. #define FOR_tcpsvd
  34. #include "toys.h"
  35. GLOBALS(
  36. char *name;
  37. char *user;
  38. long bn;
  39. char *nmsg;
  40. long cn;
  41. int maxc;
  42. int count_all;
  43. int udp;
  44. )
  45. struct list_pid {
  46. struct list_pid *next;
  47. char *ip;
  48. int pid;
  49. };
  50. struct list {
  51. struct list* next;
  52. char *d;
  53. int count;
  54. };
  55. struct hashed {
  56. struct list *head;
  57. };
  58. #define HASH_NR 256
  59. struct hashed h[HASH_NR];
  60. struct list_pid *pids = NULL;
  61. // convert IP address to string.
  62. static char *sock_to_address(struct sockaddr *sock, int flags)
  63. {
  64. char hbuf[NI_MAXHOST] = {0,};
  65. char sbuf[NI_MAXSERV] = {0,};
  66. int status = 0;
  67. socklen_t len = sizeof(struct sockaddr_in6);
  68. if (!(status = getnameinfo(sock, len, hbuf, sizeof(hbuf), sbuf,
  69. sizeof(sbuf), flags))) {
  70. if (flags & NI_NUMERICSERV) return xmprintf("%s:%s",hbuf, sbuf);
  71. return xmprintf("%s",hbuf);
  72. }
  73. error_exit("getnameinfo: %s", gai_strerror(status));
  74. }
  75. // Insert pid, ip and fd in the list.
  76. static void insert(struct list_pid **l, int pid, char *addr)
  77. {
  78. struct list_pid *newnode = xmalloc(sizeof(struct list_pid));
  79. newnode->pid = pid;
  80. newnode->ip = addr;
  81. newnode->next = NULL;
  82. if (!*l) *l = newnode;
  83. else {
  84. newnode->next = (*l);
  85. *l = newnode;
  86. }
  87. }
  88. // Hashing of IP address.
  89. static int haship( char *addr)
  90. {
  91. uint32_t ip[8] = {0,};
  92. int count = 0, i = 0;
  93. if (!addr) error_exit("NULL ip");
  94. while (i < strlen(addr)) {
  95. while (addr[i] && (addr[i] != ':') && (addr[i] != '.')) {
  96. ip[count] = ip[count]*10 + (addr[i]-'0');
  97. i++;
  98. }
  99. if (i >= strlen(addr)) break;
  100. count++;
  101. i++;
  102. }
  103. return (ip[0]^ip[1]^ip[2]^ip[3]^ip[4]^ip[5]^ip[6]^ip[7])%HASH_NR;
  104. }
  105. // Remove a node from the list.
  106. static char *delete(struct list_pid **pids, int pid)
  107. {
  108. struct list_pid *prev, *free_node, *head = *pids;
  109. char *ip = NULL;
  110. if (!head) return NULL;
  111. prev = free_node = NULL;
  112. while (head) {
  113. if (head->pid == pid) {
  114. ip = head->ip;
  115. free_node = head;
  116. if (!prev) *pids = head->next;
  117. else prev->next = head->next;
  118. free(free_node);
  119. return ip;
  120. }
  121. prev = head;
  122. head = head->next;
  123. }
  124. return NULL;
  125. }
  126. // decrement the ref count fora connection, if count reches ZERO then remove the node
  127. static void remove_connection(char *ip)
  128. {
  129. struct list *head, *prev = NULL, *free_node = NULL;
  130. int hash = haship(ip);
  131. head = h[hash].head;
  132. while (head) {
  133. if (!strcmp(ip, head->d)) {
  134. head->count--;
  135. free_node = head;
  136. if (!head->count) {
  137. if (!prev) h[hash].head = head->next;
  138. else prev->next = head->next;
  139. free(free_node);
  140. }
  141. break;
  142. }
  143. prev = head;
  144. head = head->next;
  145. }
  146. free(ip);
  147. }
  148. // Handler function.
  149. static void handle_exit(int sig)
  150. {
  151. int status;
  152. pid_t pid_n = wait(&status);
  153. if (pid_n <= 0) return;
  154. char *ip = delete(&pids, pid_n);
  155. if (!ip) return;
  156. remove_connection(ip);
  157. TT.count_all--;
  158. if (toys.optflags & FLAG_v) {
  159. if (WIFEXITED(status))
  160. xprintf("%s: end %d exit %d\n",toys.which->name, pid_n, WEXITSTATUS(status));
  161. else if (WIFSIGNALED(status))
  162. xprintf("%s: end %d signaled %d\n",toys.which->name, pid_n, WTERMSIG(status));
  163. if (TT.cn > 1) xprintf("%s: status %d/%d\n",toys.which->name, TT.count_all, TT.cn);
  164. }
  165. }
  166. // Grab uid and gid
  167. static void get_uidgid(uid_t *uid, gid_t *gid, char *ug)
  168. {
  169. struct passwd *pass = NULL;
  170. struct group *grp = NULL;
  171. char *user = NULL, *group = NULL;
  172. unsigned int n;
  173. user = ug;
  174. group = strchr(ug,':');
  175. if (group) {
  176. *group = '\0';
  177. group++;
  178. }
  179. if (!(pass = getpwnam(user))) {
  180. n = atolx_range(user, 0, INT_MAX);
  181. if (!(pass = getpwuid(n))) perror_exit("Invalid user '%s'", user);
  182. }
  183. *uid = pass->pw_uid;
  184. *gid = pass->pw_gid;
  185. if (group) {
  186. if (!(grp = getgrnam(group))) {
  187. n = atolx_range(group, 0, INT_MAX);
  188. if (!(grp = getgrgid(n))) perror_exit("Invalid group '%s'",group);
  189. }
  190. }
  191. if (grp) *gid = grp->gr_gid;
  192. }
  193. // Bind socket.
  194. static int create_bind_sock(char *host, struct sockaddr *haddr)
  195. {
  196. struct addrinfo hints, *res = NULL, *rp;
  197. int sockfd, ret, set = 1;
  198. char *ptr;
  199. unsigned long port;
  200. errno = 0;
  201. port = strtoul(toys.optargs[1], &ptr, 10);
  202. if (errno || port > 65535)
  203. error_exit("Invalid port, Range is [0-65535]");
  204. if (*ptr) ptr = toys.optargs[1];
  205. else {
  206. sprintf(toybuf, "%lu", port);
  207. ptr = toybuf;
  208. }
  209. memset(&hints, 0, sizeof hints);
  210. hints.ai_family = AF_UNSPEC;
  211. hints.ai_socktype = ((TT.udp) ?SOCK_DGRAM : SOCK_STREAM);
  212. if ((ret = getaddrinfo(host, ptr, &hints, &res)))
  213. perror_exit("%s", gai_strerror(ret));
  214. for (rp = res; rp; rp = rp->ai_next)
  215. if ( (rp->ai_family == AF_INET) || (rp->ai_family == AF_INET6)) break;
  216. if (!rp) error_exit("Invalid IP %s", host);
  217. sockfd = xsocket(rp->ai_family, TT.udp ?SOCK_DGRAM :SOCK_STREAM, 0);
  218. setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));
  219. if (TT.udp) setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &set, sizeof(set));
  220. xbind(sockfd, rp->ai_addr, rp->ai_addrlen);
  221. if(haddr) memcpy(haddr, rp->ai_addr, rp->ai_addrlen);
  222. freeaddrinfo(res);
  223. return sockfd;
  224. }
  225. static void handle_signal(int sig)
  226. {
  227. if (toys.optflags & FLAG_v) xprintf("got signal %d, exit\n", sig);
  228. raise(sig);
  229. _exit(sig + 128); //should not reach here
  230. }
  231. void tcpsvd_main(void)
  232. {
  233. uid_t uid = 0;
  234. gid_t gid = 0;
  235. pid_t pid;
  236. char haddr[sizeof(struct sockaddr_in6)];
  237. struct list *head, *newnode;
  238. int hash, fd, newfd, j;
  239. char *ptr = NULL, *addr, *server, buf[sizeof(struct sockaddr_in6)];
  240. socklen_t len = sizeof(buf);
  241. TT.udp = (*toys.which->name == 'u');
  242. if (TT.udp) toys.optflags &= ~FLAG_C;
  243. memset(buf, 0, len);
  244. if (toys.optflags & FLAG_C) {
  245. if ((ptr = strchr(TT.nmsg, ':'))) {
  246. *ptr = '\0';
  247. ptr++;
  248. }
  249. TT.maxc = atolx_range(TT.nmsg, 1, INT_MAX);
  250. }
  251. fd = create_bind_sock(toys.optargs[0], (struct sockaddr*)&haddr);
  252. if(toys.optflags & FLAG_u) {
  253. get_uidgid(&uid, &gid, TT.user);
  254. setuid(uid);
  255. setgid(gid);
  256. }
  257. if (!TT.udp && (listen(fd, TT.bn) < 0)) perror_exit("Listen failed");
  258. server = sock_to_address((struct sockaddr*)&haddr, NI_NUMERICHOST|NI_NUMERICSERV);
  259. if (toys.optflags & FLAG_v) {
  260. if (toys.optflags & FLAG_u)
  261. xprintf("%s: listening on %s, starting, uid %u, gid %u\n"
  262. ,toys.which->name, server, uid, gid);
  263. else
  264. xprintf("%s: listening on %s, starting\n", toys.which->name, server);
  265. }
  266. for (j = 0; j < HASH_NR; j++) h[j].head = NULL;
  267. sigatexit(handle_signal);
  268. signal(SIGCHLD, handle_exit);
  269. while (1) {
  270. if (TT.count_all < TT.cn) {
  271. if (TT.udp) {
  272. if(recvfrom(fd, NULL, 0, MSG_PEEK, (struct sockaddr *)buf, &len) < 0)
  273. perror_exit("recvfrom");
  274. newfd = fd;
  275. } else {
  276. newfd = accept(fd, (struct sockaddr *)buf, &len);
  277. if (newfd < 0) perror_exit("Error on accept");
  278. }
  279. } else {
  280. sigset_t ss;
  281. sigemptyset(&ss);
  282. sigsuspend(&ss);
  283. continue;
  284. }
  285. TT.count_all++;
  286. addr = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST);
  287. hash = haship(addr);
  288. if (toys.optflags & FLAG_C) {
  289. for (head = h[hash].head; head; head = head->next)
  290. if (!strcmp(head->d, addr)) break;
  291. if (head && head->count >= TT.maxc) {
  292. if (ptr) write(newfd, ptr, strlen(ptr)+1);
  293. close(newfd);
  294. TT.count_all--;
  295. continue;
  296. }
  297. }
  298. newnode = (struct list*)xzalloc(sizeof(struct list));
  299. newnode->d = addr;
  300. for (head = h[hash].head; head; head = head->next) {
  301. if (!strcmp(addr, head->d)) {
  302. head->count++;
  303. free(newnode);
  304. break;
  305. }
  306. }
  307. if (!head) {
  308. newnode->next = h[hash].head;
  309. h[hash].head = newnode;
  310. h[hash].head->count++;
  311. }
  312. if (!(pid = xfork())) {
  313. char *serv = NULL, *clie = NULL;
  314. char *client = sock_to_address((struct sockaddr*)buf, NI_NUMERICHOST | NI_NUMERICSERV);
  315. if (toys.optflags & FLAG_h) { //lookup name
  316. if (toys.optflags & FLAG_l) serv = xstrdup(TT.name);
  317. else serv = sock_to_address((struct sockaddr*)&haddr, 0);
  318. clie = sock_to_address((struct sockaddr*)buf, 0);
  319. }
  320. if (!(toys.optflags & FLAG_E)) {
  321. setenv("PROTO", TT.udp ?"UDP" :"TCP", 1);
  322. setenv("PROTOLOCALADDR", server, 1);
  323. setenv("PROTOREMOTEADDR", client, 1);
  324. if (toys.optflags & FLAG_h) {
  325. setenv("PROTOLOCALHOST", serv, 1);
  326. setenv("PROTOREMOTEHOST", clie, 1);
  327. }
  328. if (!TT.udp) {
  329. char max_c[32];
  330. sprintf(max_c, "%d", TT.maxc);
  331. setenv("TCPCONCURRENCY", max_c, 1); //Not valid for udp
  332. }
  333. }
  334. if (toys.optflags & FLAG_v) {
  335. xprintf("%s: start %d %s-%s",toys.which->name, getpid(), server, client);
  336. if (toys.optflags & FLAG_h) xprintf(" (%s-%s)", serv, clie);
  337. xputc('\n');
  338. if (TT.cn > 1)
  339. xprintf("%s: status %d/%d\n",toys.which->name, TT.count_all, TT.cn);
  340. }
  341. free(client);
  342. if (toys.optflags & FLAG_h) {
  343. free(serv);
  344. free(clie);
  345. }
  346. if (TT.udp) xconnect(newfd, (struct sockaddr *)buf, sizeof(buf));
  347. close(0);
  348. close(1);
  349. dup2(newfd, 0);
  350. dup2(newfd, 1);
  351. xexec(toys.optargs+2); //skip IP PORT
  352. } else {
  353. insert(&pids, pid, addr);
  354. xclose(newfd); //close and reopen for next client.
  355. if (TT.udp) fd = create_bind_sock(toys.optargs[0],
  356. (struct sockaddr*)&haddr);
  357. }
  358. } //while(1)
  359. }