netserve.c (6674B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * netserve: proxy server for stdio. 8 * 9 * This daemon connects a network socket to stdio of a local 10 * application through a pseudo TTY, thus enabling access to 11 * the application from a telnet client. Terminating the 12 * telnet session does not terminate the application, i.e. 13 * it is possible to connect several times to the same 14 * instance of the application. 15 * 16 * Example launching bcm.user on port 5611: 17 * 18 * netserve -d 5611 bcm.user 19 * 20 * Now connect to bcm.user, e.g. like this: 21 * 22 * telnet 127.0.0.1 5611 23 */ 24 25 #include <stdlib.h> 26 #include <stdio.h> 27 #include <string.h> 28 29 #include <sys/types.h> 30 #include <sys/socket.h> 31 #include <sys/time.h> 32 #include <sys/wait.h> 33 34 #include <unistd.h> 35 #include <signal.h> 36 #include <pthread.h> 37 #include <pty.h> 38 #include <arpa/inet.h> 39 40 /* Telnet definitions */ 41 #define T_CMD 255 42 #define T_DONT 254 43 #define T_DO 253 44 #define T_WONT 252 45 #define T_WILL 251 46 #define T_ECHO 1 47 #define T_SGA 3 48 49 static unsigned char will_sga[] = { T_CMD, T_WILL, T_SGA }; 50 static unsigned char will_echo[] = { T_CMD, T_WILL, T_ECHO }; 51 static unsigned char dont_echo[] = { T_CMD, T_DONT, T_ECHO }; 52 53 /* Network server */ 54 static volatile int _netfd = -1; 55 static int _server_socket; 56 57 static int 58 _setup_socket(int port) 59 { 60 struct sockaddr_in addr; 61 int i; 62 int sockfd; 63 64 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 65 perror("server: can't open stream socket"); 66 exit(1); 67 } 68 69 /* 70 * Set socket option to reuse local address. This is supposed 71 * to have the effect of freeing up the local address. 72 */ 73 i = 1; 74 if (setsockopt(sockfd, SOL_SOCKET, 75 SO_REUSEADDR, (char *) &i, 4) < 0) { 76 perror("setsockopt"); 77 } 78 79 /* Set up server address */ 80 memset((void *) &addr,0x0, sizeof(addr)); 81 addr.sin_family = AF_INET; 82 addr.sin_addr.s_addr = htonl(INADDR_ANY); 83 addr.sin_port = htons(port); 84 85 /* Bind our local address and port */ 86 if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 87 perror("server: can't bind local address"); 88 exit(1); 89 } 90 91 /* Only process one connection at a time */ 92 listen(sockfd, 1); 93 94 return sockfd; 95 } 96 97 static int 98 _wait_for_connection(int sockfd) 99 { 100 struct sockaddr_in addr; 101 socklen_t len; 102 int newsockfd; 103 104 len = sizeof(addr); 105 if ((newsockfd = accept(sockfd, (struct sockaddr *) &addr, &len)) < 0) { 106 perror("accept"); 107 exit(1); 108 } 109 110 /* Telnet response required for correct interaction with app */ 111 write(newsockfd, will_sga, sizeof(will_sga)); 112 write(newsockfd, will_echo, sizeof(will_echo)); 113 write(newsockfd, dont_echo, sizeof(dont_echo)); 114 115 return newsockfd; 116 } 117 118 static void 119 _do_telnet(int ifd, int ofd) 120 { 121 unsigned char cmd[8]; 122 int i; 123 124 /* Read command */ 125 for (i = 1; i < 3; i++) { 126 if (read(ifd, &cmd[i], 1) != 1) { 127 return; 128 } 129 } 130 131 /* Select negative response */ 132 switch (cmd[1]) { 133 case T_WILL: 134 case T_WONT: 135 cmd[1] = T_DONT; 136 break; 137 case T_DO: 138 case T_DONT: 139 cmd[1] = T_WONT; 140 break; 141 default: 142 /* Ignore */ 143 return; 144 } 145 146 /* Ignore responses to our own commands */ 147 switch (cmd[2]) { 148 case T_ECHO: 149 case T_SGA: 150 return; 151 } 152 153 /* Send response */ 154 cmd[0] = T_CMD; 155 write(ofd, &cmd, 3); 156 } 157 158 static void * 159 _net2tty(void *arg) 160 { 161 int fd = *((int *)arg); 162 unsigned char data; 163 164 while (1) { 165 while (_netfd < 0) { 166 usleep(100000); 167 } 168 if (read(_netfd, &data, 1) != 1) { 169 /* Broken pipe -- client quit */ 170 close(_netfd); 171 _netfd = -1; 172 raise(SIGUSR2); 173 } else if (data == T_CMD) { 174 _do_telnet(_netfd, fd); 175 } else { 176 write(fd, &data, 1); 177 fsync(fd); 178 } 179 } 180 return NULL; 181 } 182 183 static void * 184 _tty2net(void *arg) 185 { 186 int fd = *((int *)arg); 187 unsigned char data; 188 189 while (1) { 190 if (read(fd, &data, 1) != 1) { 191 /* Broken pipe -- app quit */ 192 close(fd); 193 break; 194 } 195 if (_netfd >= 0) { 196 write(_netfd, &data, 1); 197 fsync(_netfd); 198 } 199 } 200 return NULL; 201 } 202 203 static int 204 _start_app(char **args, int s) 205 { 206 pid_t pid; 207 208 if ((pid = fork()) < 0) { 209 perror ("fork"); 210 return -1; 211 } 212 if (pid > 0) { 213 return pid; 214 } 215 dup2(s, 0); 216 dup2(s, 1); 217 dup2(s, 2); 218 execv(*args, args); 219 exit(0); 220 } 221 222 static void 223 _restart(void) 224 { 225 int s; 226 227 if ((s = _wait_for_connection(_server_socket)) < 0) { 228 fprintf(stderr, "connection error\n"); 229 exit(0); 230 } 231 /* Reenable the proxies with the new fd */ 232 _netfd = s; 233 } 234 235 static void 236 _sig(int sig) 237 { 238 if (sig == SIGUSR2) { 239 _restart(); 240 } 241 } 242 243 int 244 main(int argc, char *argv[]) 245 { 246 int p; 247 int pid; 248 int do_fork = 0; 249 int ttyfd, appfd; 250 pthread_t id; 251 252 argv++; 253 254 if (!*argv || !strcmp(*argv, "--help") || !strcmp(*argv, "-h")) { 255 printf("usage: netserve [-d] <port> <program> [args]\n"); 256 exit(0); 257 } 258 259 if (!strcmp(*argv, "-d")) { 260 do_fork = 1; 261 argv++; 262 } 263 264 if ((p = atoi(*argv++)) == 0) { 265 fprintf(stderr, "bad port number\n"); 266 exit(0); 267 } 268 269 if (do_fork) { 270 /* Daemonize */ 271 pid = fork(); 272 if (pid < 0) { 273 perror("fork()"); 274 exit(0); 275 } 276 if (pid > 0) { 277 exit(0); 278 } 279 setsid(); 280 } 281 282 /* Broken pipes are not a problem */ 283 signal(SIGPIPE, SIG_IGN); 284 285 /* Get a pseudo tty */ 286 if (openpty(&ttyfd, &appfd, NULL, NULL, NULL) < 0) { 287 perror("open pty"); 288 exit(0); 289 } 290 291 /* Start the application up with sv[1] as its stdio */ 292 pid = _start_app(argv, appfd); 293 294 /* Start proxy for input */ 295 if (pthread_create(&id, NULL, _net2tty, (void *)&ttyfd) < 0) { 296 perror("pthread_create"); 297 exit(0); 298 } 299 300 /* Start proxy for output */ 301 if (pthread_create(&id, NULL, _tty2net, (void *)&ttyfd) < 0) { 302 perror("pthread_create"); 303 exit(0); 304 } 305 306 /* Setup server */ 307 _server_socket = _setup_socket(p); 308 309 /* SIGUSR2 restarts the server when the client connection closes */ 310 signal(SIGUSR2, _sig); 311 312 /* Start the first server */ 313 raise(SIGUSR2); 314 315 /* Wait for our child to exit */ 316 waitpid(pid, &p, 0); 317 318 return 0; 319 }