static void parse_url(const char *src_url, struct host_info *h) { char *url, *p, *sp; free(h->allocated); h->allocated = url = xstrdup(src_url); if (strncmp(url, "http://", 7) == 0) { h->port = bb_lookup_port("http", "tcp", 80); h->host = url + 7; h->is_ftp = 0; } else if (strncmp(url, "ftp://", 6) == 0) { h->port = bb_lookup_port("ftp", "tcp", 21); h->host = url + 6; h->is_ftp = 1; } else bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url)); // FYI: // "Real" wget 'http://busybox.net?var=a/b' sends this request: // 'GET /?var=a/b HTTP 1.0' // and saves 'index.html?var=a%2Fb' (we save 'b') // wget 'http://busybox.net?login=john@doe': // request: 'GET /?login=john@doe HTTP/1.0' // saves: 'index.html?login=john@doe' (we save '?login=john@doe') // wget 'http://busybox.net#test/test': // request: 'GET / HTTP/1.0' // saves: 'index.html' (we save 'test') // // We also don't add unique .N suffix if file exists... sp = strchr(h->host, '/'); p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p; p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p; if (!sp) { h->path = ""; } else if (*sp == '/') { *sp = '\0'; h->path = sp + 1; } else { // '#' or '?' // http://busybox.net?login=john@doe is a valid URL // memmove converts to: // http:/busybox.nett?login=john@doe... memmove(h->host - 1, h->host, sp - h->host); h->host--; sp[-1] = '\0'; h->path = sp; } // We used to set h->user to NULL here, but this interferes // with handling of code 302 ("object was moved") sp = strrchr(h->host, '@'); if (sp != NULL) { h->user = h->host; *sp = '\0'; h->host = sp + 1; } sp = h->host; }
static time_t askremotedate(const char *host) { unsigned long int nett, localt; struct sockaddr_in s_in; int fd; bb_lookup_host(&s_in, host); s_in.sin_port = bb_lookup_port("time", "tcp", 37); /* Add a timeout for dead or non accessable servers */ alarm(10); signal(SIGALRM, socket_timeout); fd = xconnect(&s_in); if (safe_read(fd, (void *)&nett, 4) != 4) /* read time from server */ bb_error_msg_and_die("%s did not send the complete time", host); close(fd); /* convert from network byte order to local byte order. * RFC 868 time is the number of seconds * since 00:00 (midnight) 1 January 1900 GMT * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT * Subtract the RFC 868 time to get Linux epoch */ localt= ntohl(nett) - RFC_868_BIAS; return(localt); }
int fakeidentd_main(int argc, char **argv) { enum { OPT_foreground = 0x1, OPT_inetd = 0x2, OPT_inetdwait = 0x4, OPT_fiw = 0x7, OPT_bindaddr = 0x8, }; const char *bind_address = NULL; unsigned opt; int fd; opt = getopt32(argv, "fiwb:", &bind_address); strcpy(bogouser, "nobody"); if (argv[optind]) strncpy(bogouser, argv[optind], sizeof(bogouser)); /* Daemonize if no -f and no -i and no -w */ if (!(opt & OPT_fiw)); bb_daemonize_or_rexec(0, argv); /* Where to log in inetd modes? "Classic" inetd * probably has its stderr /dev/null'ed (we need log to syslog?), * but daemontools-like utilities usually expect that children * log to stderr. I like daemontools more. Go their way. * (Or maybe we need yet another option "log to syslog") */ if (!(opt & OPT_fiw) /* || (opt & OPT_syslog) */) { openlog(applet_name, 0, LOG_DAEMON); logmode = LOGMODE_SYSLOG; } if (opt & OPT_inetd) { inetd_mode(); return 0; } /* Ignore closed connections when writing */ signal(SIGPIPE, SIG_IGN); fd = 0; if (!(opt & OPT_inetdwait)) { fd = create_and_bind_stream_or_die(bind_address, bb_lookup_port("identd", "tcp", 113)); xlisten(fd, 5); } isrv_run(fd, new_peer, do_rd, /*do_wr:*/ NULL, do_timeout, TIMEOUT, (opt & OPT_inetdwait) ? TIMEOUT : 0); return 0; }
static time_t askremotedate(const char *host) { uint32_t nett; int fd; /* Add a timeout for dead or inaccessible servers */ alarm(10); signal(SIGALRM, socket_timeout); fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37)); if (safe_read(fd, (void *)&nett, 4) != 4) /* read time from server */ bb_error_msg_and_die("%s did not send the complete time", host); close(fd); /* convert from network byte order to local byte order. * RFC 868 time is the number of seconds * since 00:00 (midnight) 1 January 1900 GMT * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT * Subtract the RFC 868 time to get Linux epoch */ return ntohl(nett) - RFC_868_BIAS; }
int ftpgetput_main(int argc, char **argv) { /* content-length of the file */ unsigned long opt; char *port = "ftp"; /* socket to ftp server */ FILE *control_stream; struct sockaddr_in s_in; /* continue a prev transfer (-c) */ ftp_host_info_t *server; int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = NULL; /* Check to see if the command is ftpget or ftput */ #ifdef CONFIG_FTPPUT # ifdef CONFIG_FTPGET if (bb_applet_name[3] == 'p') { ftp_action = ftp_send; } # else ftp_action = ftp_send; # endif #endif #ifdef CONFIG_FTPGET # ifdef CONFIG_FTPPUT if (bb_applet_name[3] == 'g') { ftp_action = ftp_recieve; } # else ftp_action = ftp_recieve; # endif #endif /* Set default values */ server = xmalloc(sizeof(ftp_host_info_t)); server->user = "******"; server->password = "******"; verbose_flag = 0; /* * Decipher the command line */ bb_applet_long_options = ftpgetput_long_options; opt = bb_getopt_ulflags(argc, argv, "cvu:p:P:", &server->user, &server->password, &port); /* Process the non-option command line arguments */ if (argc - optind != 3) { bb_show_usage(); } if (opt & FTPGETPUT_OPT_CONTINUE) { do_continue = 1; } if (opt & FTPGETPUT_OPT_VERBOSE) { verbose_flag = 1; } /* We want to do exactly _one_ DNS lookup, since some * sites (i.e. ftp.us.debian.org) use round-robin DNS * and we want to connect to only one IP... */ server->s_in = &s_in; bb_lookup_host(&s_in, argv[optind]); s_in.sin_port = bb_lookup_port(port, "tcp", 21); if (verbose_flag) { printf("Connecting to %s[%s]:%d\n", argv[optind], inet_ntoa(s_in.sin_addr), ntohs(s_in.sin_port)); } /* Connect/Setup/Configure the FTP session */ control_stream = ftp_login(server); return(ftp_action(server, control_stream, argv[optind + 1], argv[optind + 2])); }
static void parse_url(const char *src_url, struct host_info *h) { char *url, *p, *sp; free(h->allocated); h->allocated = url = xstrdup(src_url); if (strncmp(url, "http://", 7) == 0) { h->port = bb_lookup_port("http", "tcp", 80); h->host = url + 7; h->is_ftp = 0; } else if (strncmp(url, "ftp://", 6) == 0) { h->port = bb_lookup_port("ftp", "tcp", 21); h->host = url + 6; h->is_ftp = 1; } else bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url)); // FYI: // "Real" wget 'http://busybox.net?var=a/b' sends this request: // 'GET /?var=a/b HTTP 1.0' // and saves 'index.html?var=a%2Fb' (we save 'b') // wget 'http://busybox.net?login=john@doe': // request: 'GET /?login=john@doe HTTP/1.0' // saves: 'index.html?login=john@doe' (we save '?login=john@doe') // wget 'http://busybox.net#test/test': // request: 'GET / HTTP/1.0' // saves: 'index.html' (we save 'test') // // We also don't add unique .N suffix if file exists... sp = strchr(h->host, '/'); p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p; p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p; if (!sp) { h->path = ""; } else if (*sp == '/') { *sp = '\0'; h->path = sp + 1; } else { // '#' or '?' // http://busybox.net?login=john@doe is a valid URL // memmove converts to: // http:/busybox.nett?login=john@doe... memmove(h->host - 1, h->host, sp - h->host); h->host--; sp[-1] = '\0'; h->path = sp; } // We used to set h->user to NULL here, but this interferes // with handling of code 302 ("object was moved") sp = strrchr(h->host, '@'); if (sp != NULL) { // URL-decode "user:password" string before base64-encoding: // wget http://test:my%[email protected] should send // Authorization: Basic dGVzdDpteSBwYXNz // which decodes to "test:my pass". // Standard wget and curl do this too. *sp = '\0'; h->user = percent_decode_in_place(h->host, /*strict:*/ 0); h->host = sp + 1; } sp = h->host; }
int fakeidentd_main(int argc, char **argv) { int fd; pid_t pid; /* FD_ZERO(&G.readfds); - in bss, already zeroed */ FD_SET(0, &G.readfds); /* handle -b <ip> parameter */ getopt32(argc, argv, "b:", &bind_ip_address); /* handle optional REPLY STRING */ if (optind < argc) G.identuser = argv[optind]; else G.identuser = "******"; writepid(); signal(SIGTERM, handlexitsigs); signal(SIGINT, handlexitsigs); signal(SIGQUIT, handlexitsigs); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); /* ignore closed connections when writing */ fd = create_and_bind_stream_or_die(bind_ip_address, bb_lookup_port("identd", "tcp", 113)); xlisten(fd, 5); pid = fork(); if (pid < 0) bb_perror_msg_and_die("fork"); if (pid != 0) /* parent */ exit(0); /* child */ setsid(); movefd(fd, 0); while (fd) close(fd--); openlog(applet_name, 0, LOG_DAEMON); logmode = LOGMODE_SYSLOG; /* main loop where we process all events and never exit */ while (1) { fd_set rfds = G.readfds; struct timeval tv = { 15, 0 }; int i; int tim = time(NULL); select(G.conncnt + FCS, &rfds, NULL, NULL, G.conncnt? &tv: NULL); for (i = G.conncnt - 1; i >= 0; i--) { int s = i + FCS; if (FD_ISSET(s, &rfds)) { char *buf = conns[i].buf; unsigned len = conns[i].len; unsigned l; l = read(s, buf + len, sizeof(conns[0].buf) - len); if (l > 0) { if (checkInput(buf, len, l)) { reply(s, buf); goto deleteconn; } else if (len + l >= sizeof(conns[0].buf)) { replyError(s, "X-INVALID-REQUEST"); goto deleteconn; } else { conns[i].len += l; } } else { goto deleteconn; } conns[i].lasttime = tim; continue; deleteconn: deleteConn(s); } else { /* implement as time_after() in linux kernel sources ... */ if (conns[i].lasttime + MAXIDLETIME <= tim) { replyError(s, "X-TIMEOUT"); deleteConn(s); } } } if (FD_ISSET(0, &rfds)) { int s = accept(0, NULL, 0); if (s < 0) { if (errno != EINTR) bb_perror_msg("accept"); } else { if (G.conncnt == MAXCONNS) i = closeOldest(); else i = G.conncnt++; movefd(s, i + FCS); /* move if not already there */ FD_SET(i + FCS, &G.readfds); conns[i].len = 0; conns[i].lasttime = time(NULL); } } } /* end of while (1) */ return 0; }
extern int telnet_main(int argc, char** argv) { int len; struct sockaddr_in s_in; #ifdef USE_POLL struct pollfd ufds[2]; #else fd_set readfds; int maxfd; #endif #ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN int opt; #endif #ifdef CONFIG_FEATURE_AUTOWIDTH get_terminal_width_height(0, &win_width, &win_height); #endif #ifdef CONFIG_FEATURE_TELNET_TTYPE ttype = getenv("TERM"); #endif memset(&G, 0, sizeof G); if (tcgetattr(0, &G.termios_def) < 0) exit(1); G.termios_raw = G.termios_def; cfmakeraw(&G.termios_raw); if (argc < 2) bb_show_usage(); #ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN autologin = NULL; while ((opt = getopt(argc, argv, "al:")) != EOF) { switch (opt) { case 'l': autologin = optarg; break; case 'a': autologin = getenv("USER"); break; case '?': bb_show_usage(); break; } } if (optind < argc) { bb_lookup_host(&s_in, argv[optind++]); s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] : "telnet", "tcp", 23); if (optind < argc) bb_show_usage(); } else bb_show_usage(); #else bb_lookup_host(&s_in, argv[1]); s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23); #endif G.netfd = xconnect(&s_in); setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); signal(SIGINT, fgotsig); #ifdef USE_POLL ufds[0].fd = 0; ufds[1].fd = G.netfd; ufds[0].events = ufds[1].events = POLLIN; #else FD_ZERO(&readfds); FD_SET(0, &readfds); FD_SET(G.netfd, &readfds); maxfd = G.netfd + 1; #endif while (1) { #ifndef USE_POLL fd_set rfds = readfds; switch (select(maxfd, &rfds, NULL, NULL, NULL)) #else switch (poll(ufds, 2, -1)) #endif { case 0: /* timeout */ case -1: /* error, ignore and/or log something, bay go to loop */ if (G.gotsig) conescape(); else sleep(1); break; default: #ifdef USE_POLL if (ufds[0].revents) /* well, should check POLLIN, but ... */ #else if (FD_ISSET(0, &rfds)) #endif { len = read(0, G.buf, DATABUFSIZE); if (len <= 0) doexit(0); TRACE(0, ("Read con: %d\n", len)); handlenetoutput(len); } #ifdef USE_POLL if (ufds[1].revents) /* well, should check POLLIN, but ... */ #else if (FD_ISSET(G.netfd, &rfds)) #endif { len = read(G.netfd, G.buf, DATABUFSIZE); if (len <= 0) { WriteCS(1, "Connection closed by foreign host.\r\n"); doexit(1); } TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len)); handlenetinput(len); } } } }
int nc_main(int argc, char **argv) { int do_listen = 0, lport = 0, delay = 0, wsecs = 0, tmpfd, opt, sfd, x; #ifdef CONFIG_NC_GAPING_SECURITY_HOLE char *pr00gie = NULL; #endif struct sockaddr_in address; struct hostent *hostinfo; fd_set readfds, testfds; while ((opt = getopt(argc, argv, "lp:i:e:w:")) > 0) { switch (opt) { case 'l': do_listen++; break; case 'p': lport = bb_lookup_port(optarg, "tcp", 0); break; case 'i': delay = atoi(optarg); break; #ifdef CONFIG_NC_GAPING_SECURITY_HOLE case 'e': pr00gie = optarg; break; #endif case 'w': wsecs = atoi(optarg); break; default: bb_show_usage(); } } if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc)) bb_show_usage(); sfd = bb_xsocket(AF_INET, SOCK_STREAM, 0); x = 1; if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1) bb_perror_msg_and_die("reuseaddr"); address.sin_family = AF_INET; if (wsecs) { signal(SIGALRM, timeout); alarm(wsecs); } if (lport != 0) { memset(&address.sin_addr, 0, sizeof(address.sin_addr)); address.sin_port = lport; bb_xbind(sfd, (struct sockaddr *) &address, sizeof(address)); } if (do_listen) { socklen_t addrlen = sizeof(address); bb_xlisten(sfd, 1); if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0) bb_perror_msg_and_die("accept"); close(sfd); sfd = tmpfd; } else { hostinfo = xgethostbyname(argv[optind]); address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; address.sin_port = bb_lookup_port(argv[optind+1], "tcp", 0); if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) bb_perror_msg_and_die("connect"); } if (wsecs) { alarm(0); signal(SIGALRM, SIG_DFL); } #ifdef CONFIG_NC_GAPING_SECURITY_HOLE /* -e given? */ if (pr00gie) { dup2(sfd, 0); close(sfd); dup2(0, 1); dup2(0, 2); execl(pr00gie, pr00gie, NULL); /* Don't print stuff or it will go over the wire.... */ _exit(-1); } #endif /* CONFIG_NC_GAPING_SECURITY_HOLE */ FD_ZERO(&readfds); FD_SET(sfd, &readfds); FD_SET(STDIN_FILENO, &readfds); while (1) { int fd; int ofd; int nread; testfds = readfds; if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0) bb_perror_msg_and_die("select"); for (fd = 0; fd < FD_SETSIZE; fd++) { if (FD_ISSET(fd, &testfds)) { if ((nread = safe_read(fd, bb_common_bufsiz1, sizeof(bb_common_bufsiz1))) < 0) { bb_perror_msg_and_die(bb_msg_read_error); } if (fd == sfd) { if (nread == 0) exit(0); ofd = STDOUT_FILENO; } else { if (nread <= 0) { shutdown(sfd, 1 /* send */ ); close(STDIN_FILENO); FD_CLR(STDIN_FILENO, &readfds); } ofd = sfd; } if (bb_full_write(ofd, bb_common_bufsiz1, nread) < 0) bb_perror_msg_and_die(bb_msg_write_error); if (delay > 0) { sleep(delay); } } } } }
int telnet_main(int argc, char **argv) { char *host; int port; int len; #ifdef USE_POLL struct pollfd ufds[2]; #else fd_set readfds; int maxfd; #endif INIT_G(); #if ENABLE_FEATURE_AUTOWIDTH get_terminal_width_height(0, &G.win_width, &G.win_height); #endif #if ENABLE_FEATURE_TELNET_TTYPE G.ttype = getenv("TERM"); #endif if (tcgetattr(0, &G.termios_def) >= 0) { G.do_termios = 1; G.termios_raw = G.termios_def; cfmakeraw(&G.termios_raw); } if (argc < 2) bb_show_usage(); #if ENABLE_FEATURE_TELNET_AUTOLOGIN if (1 & getopt32(argv, "al:", &G.autologin)) G.autologin = getenv("USER"); argv += optind; #else argv++; #endif if (!*argv) bb_show_usage(); host = *argv++; port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23); if (*argv) /* extra params?? */ bb_show_usage(); G.netfd = create_and_connect_stream_or_die(host, port); setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); signal(SIGINT, fgotsig); #ifdef USE_POLL ufds[0].fd = 0; ufds[1].fd = G.netfd; ufds[0].events = ufds[1].events = POLLIN; #else FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); FD_SET(G.netfd, &readfds); maxfd = G.netfd + 1; #endif while (1) { #ifndef USE_POLL fd_set rfds = readfds; switch (select(maxfd, &rfds, NULL, NULL, NULL)) #else switch (poll(ufds, 2, -1)) #endif { case 0: /* timeout */ case -1: /* error, ignore and/or log something, bay go to loop */ if (G.gotsig) conescape(); else sleep(1); break; default: #ifdef USE_POLL if (ufds[0].revents) /* well, should check POLLIN, but ... */ #else if (FD_ISSET(STDIN_FILENO, &rfds)) #endif { len = read(STDIN_FILENO, G.buf, DATABUFSIZE); if (len <= 0) doexit(EXIT_SUCCESS); TRACE(0, ("Read con: %d\n", len)); handlenetoutput(len); } #ifdef USE_POLL if (ufds[1].revents) /* well, should check POLLIN, but ... */ #else if (FD_ISSET(G.netfd, &rfds)) #endif { len = read(G.netfd, G.buf, DATABUFSIZE); if (len <= 0) { write_str(1, "Connection closed by foreign host\r\n"); doexit(EXIT_FAILURE); } TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len)); handlenetinput(len); } } } }
int tftp_main(int argc, char **argv) { struct hostent *host = NULL; char *localfile = NULL; char *remotefile = NULL; int port; int cmd = 0; int fd = -1; int flags = 0; int opt; int result; int blocksize = TFTP_BLOCKSIZE_DEFAULT; /* figure out what to pass to getopt */ #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE #define BS "b:" #else #define BS #endif #ifdef CONFIG_FEATURE_TFTP_GET #define GET "g" #else #define GET #endif #ifdef CONFIG_FEATURE_TFTP_PUT #define PUT "p" #else #define PUT #endif while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) { switch (opt) { #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE case 'b': blocksize = atoi(optarg); if (!tftp_blocksize_check(blocksize, 0)) { return EXIT_FAILURE; } break; #endif #ifdef CONFIG_FEATURE_TFTP_GET case 'g': cmd = tftp_cmd_get; flags = O_WRONLY | O_CREAT; break; #endif #ifdef CONFIG_FEATURE_TFTP_PUT case 'p': cmd = tftp_cmd_put; flags = O_RDONLY; break; #endif case 'l': localfile = bb_xstrdup(optarg); break; case 'r': remotefile = bb_xstrdup(optarg); break; } } if ((cmd == 0) || (optind == argc)) { bb_show_usage(); } if(localfile && strcmp(localfile, "-") == 0) { fd = fileno((cmd==tftp_cmd_get)? stdout : stdin); } if(localfile == NULL) localfile = remotefile; if(remotefile == NULL) remotefile = localfile; if (fd==-1) { fd = open(localfile, flags, 0644); } if (fd < 0) { bb_perror_msg_and_die("local file"); } host = xgethostbyname(argv[optind]); port = bb_lookup_port(argv[optind + 1], "udp", 69); #ifdef CONFIG_FEATURE_TFTP_DEBUG printf("using server \"%s\", remotefile \"%s\", " "localfile \"%s\".\n", inet_ntoa(*((struct in_addr *) host->h_addr)), remotefile, localfile); #endif result = tftp(cmd, host, remotefile, fd, port, blocksize); #ifdef CONFIG_FEATURE_CLEAN_UP if (!(fd == fileno(stdout) || fd == fileno(stdin))) { close(fd); } #endif return(result); }
int tftp_main(int argc, char **argv) { struct hostent *host = NULL; const char *localfile = NULL; const char *remotefile = NULL; int port; int cmd = 0; int fd = -1; int flags = 0; int result; int blocksize = TFTP_BLOCKSIZE_DEFAULT; /* figure out what to pass to getopt */ #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE char *sblocksize = NULL; #define BS "b:" #define BS_ARG , &sblocksize #else #define BS #define BS_ARG #endif #ifdef CONFIG_FEATURE_TFTP_GET #define GET "g" #define GET_COMPL ":g" #else #define GET #define GET_COMP #endif #ifdef CONFIG_FEATURE_TFTP_PUT #define PUT "p" #define PUT_COMPL ":p" #else #define PUT #define PUT_COMPL #endif #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT) bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g"; #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT) bb_opt_complementally = GET_COMPL PUT_COMPL; #else /* XXX: may be should #error ? */ #endif cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS, &localfile, &remotefile BS_ARG); #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE if(sblocksize) { blocksize = atoi(sblocksize); if (!tftp_blocksize_check(blocksize, 0)) { return EXIT_FAILURE; } } #endif cmd &= (tftp_cmd_get | tftp_cmd_put); #ifdef CONFIG_FEATURE_TFTP_GET if(cmd == tftp_cmd_get) flags = O_WRONLY | O_CREAT | O_TRUNC; #endif #ifdef CONFIG_FEATURE_TFTP_PUT if(cmd == tftp_cmd_put) flags = O_RDONLY; #endif if(localfile == NULL) localfile = remotefile; if(remotefile == NULL) remotefile = localfile; /* XXX: I corrected this, but may be wrong too. vodz */ if(localfile==NULL || strcmp(localfile, "-") == 0) { fd = fileno((cmd==tftp_cmd_get)? stdout : stdin); } else if (fd==-1) { fd = open(localfile, flags, 0644); } if (fd < 0) { bb_perror_msg_and_die("local file"); } /* XXX: argv[optind] and/or argv[optind + 1] may be NULL! */ host = xgethostbyname(argv[optind]); port = bb_lookup_port(argv[optind + 1], "udp", 69); #ifdef CONFIG_FEATURE_TFTP_DEBUG fprintf(stderr, "using server \"%s\", remotefile \"%s\", " "localfile \"%s\".\n", inet_ntoa(*((struct in_addr *) host->h_addr)), remotefile, localfile); #endif result = tftp(cmd, host, remotefile, fd, port, blocksize); #ifdef CONFIG_FEATURE_CLEAN_UP if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) { close(fd); } #endif return(result); }
int tcpudpsvd_main(int argc ATTRIBUTE_UNUSED, char **argv) { char *str_C, *str_t; char *user; struct hcc *hccp; const char *instructs; char *msg_per_host = NULL; unsigned len_per_host = len_per_host; /* gcc */ #ifndef SSLSVD struct bb_uidgid_t ugid; #endif bool tcp; uint16_t local_port; char *preset_local_hostname = NULL; char *remote_hostname = remote_hostname; /* for compiler */ char *remote_addr = remote_addr; /* for compiler */ len_and_sockaddr *lsa; len_and_sockaddr local, remote; socklen_t sa_len; int pid; int sock; int conn; unsigned backlog = 20; INIT_G(); tcp = (applet_name[0] == 't'); /* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */ opt_complementary = "-3:i--i:ph:vv:b+:c+"; #ifdef SSLSVD getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose ); #else /* "+": stop on first non-option */ getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &verbose ); #endif if (option_mask32 & OPT_C) { /* -C n[:message] */ max_per_host = bb_strtou(str_C, &str_C, 10); if (str_C[0]) { if (str_C[0] != ':') bb_show_usage(); msg_per_host = str_C + 1; len_per_host = strlen(msg_per_host); } } if (max_per_host > cmax) max_per_host = cmax; if (option_mask32 & OPT_u) { if (!get_uidgid(&ugid, user, 1)) bb_error_msg_and_die("unknown user/group: %s", user); } #ifdef SSLSVD if (option_mask32 & OPT_U) ssluser = optarg; if (option_mask32 & OPT_slash) root = optarg; if (option_mask32 & OPT_Z) cert = optarg; if (option_mask32 & OPT_K) key = optarg; #endif argv += optind; if (!argv[0][0] || LONE_CHAR(argv[0], '0')) argv[0] = (char*)"0.0.0.0"; /* Per-IP flood protection is not thought-out for UDP */ if (!tcp) max_per_host = 0; bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ #ifdef SSLSVD sslser = user; client = 0; if ((getuid() == 0) && !(option_mask32 & OPT_u)) { xfunc_exitcode = 100; bb_error_msg_and_die("-U ssluser must be set when running as root"); } if (option_mask32 & OPT_u) if (!uidgid_get(&sslugid, ssluser, 1)) { if (errno) { bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser); } bb_error_msg_and_die("unknown user/group '%s'", ssluser); } if (!cert) cert = "./cert.pem"; if (!key) key = cert; if (matrixSslOpen() < 0) fatal("cannot initialize ssl"); if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { if (client) fatal("cannot read cert, key, or ca file"); fatal("cannot read cert or key file"); } if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0) fatal("cannot create ssl session"); #endif sig_block(SIGCHLD); signal(SIGCHLD, sig_child_handler); bb_signals(BB_FATAL_SIGS, sig_term_handler); signal(SIGPIPE, SIG_IGN); if (max_per_host) ipsvd_perhost_init(cmax); local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); lsa = xhost2sockaddr(argv[0], local_port); argv += 2; sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); sa_len = lsa->len; /* I presume sockaddr len stays the same */ xbind(sock, &lsa->u.sa, sa_len); if (tcp) xlisten(sock, backlog); else /* udp: needed for recv_from_to to work: */ socket_want_pktinfo(sock); /* ndelay_off(sock); - it is the default I think? */ #ifndef SSLSVD if (option_mask32 & OPT_u) { /* drop permissions */ xsetgid(ugid.gid); xsetuid(ugid.uid); } #endif if (verbose) { char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); bb_error_msg("listening on %s, starting", addr); free(addr); #ifndef SSLSVD if (option_mask32 & OPT_u) printf(", uid %u, gid %u", (unsigned)ugid.uid, (unsigned)ugid.gid); #endif } /* Main accept() loop */ again: hccp = NULL; while (cnum >= cmax) wait_for_any_sig(); /* expecting SIGCHLD */ /* Accept a connection to fd #0 */ again1: close(0); again2: sig_unblock(SIGCHLD); local.len = remote.len = sa_len; if (tcp) { conn = accept(sock, &remote.u.sa, &remote.len); } else { /* In case recv_from_to won't be able to recover local addr. * Also sets port - recv_from_to is unable to do it. */ local = *lsa; conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len); } sig_block(SIGCHLD); if (conn < 0) { if (errno != EINTR) bb_perror_msg(tcp ? "accept" : "recv"); goto again2; } xmove_fd(tcp ? conn : sock, 0); if (max_per_host) { /* Drop connection immediately if cur_per_host > max_per_host * (minimizing load under SYN flood) */ remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp); if (cur_per_host > max_per_host) { /* ipsvd_perhost_add detected that max is exceeded * (and did not store ip in connection table) */ free(remote_addr); if (msg_per_host) { /* don't block or test for errors */ send(0, msg_per_host, len_per_host, MSG_DONTWAIT); } goto again1; } /* NB: remote_addr is not leaked, it is stored in conn table */ } if (!tcp) { /* Voodoo magic: making udp sockets each receive its own * packets is not trivial, and I still not sure * I do it 100% right. * 1) we have to do it before fork() * 2) order is important - is it right now? */ /* Open new non-connected UDP socket for further clients... */ sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); /* Make plain write/send work for old socket by supplying default * destination address. This also restricts incoming packets * to ones coming from this remote IP. */ xconnect(0, &remote.u.sa, sa_len); /* hole? at this point we have no wildcard udp socket... * can this cause clients to get "port unreachable" icmp? * Yup, time window is very small, but it exists (is it?) */ /* ..."open new socket", continued */ xbind(sock, &lsa->u.sa, sa_len); socket_want_pktinfo(sock); /* Doesn't work: * we cannot replace fd #0 - we will lose pending packet * which is already buffered for us! And we cannot use fd #1 * instead - it will "intercept" all following packets, but child * does not expect data coming *from fd #1*! */ #if 0 /* Make it so that local addr is fixed to localp->u.sa * and we don't accidentally accept packets to other local IPs. */ /* NB: we possibly bind to the _very_ same_ address & port as the one * already bound in parent! This seems to work in Linux. * (otherwise we can move socket to fd #0 only if bind succeeds) */ close(0); set_nport(localp, htons(local_port)); xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0); setsockopt_reuseaddr(0); /* crucial */ xbind(0, &localp->u.sa, localp->len); #endif } pid = vfork(); if (pid == -1) { bb_perror_msg("vfork"); goto again; } if (pid != 0) { /* Parent */ cnum++; if (verbose) connection_status(); if (hccp) hccp->pid = pid; /* clean up changes done by vforked child */ undo_xsetenv(); goto again; } /* Child: prepare env, log, and exec prog */ /* Closing tcp listening socket */ if (tcp) close(sock); { /* vfork alert! every xmalloc in this block should be freed! */ char *local_hostname = local_hostname; /* for compiler */ char *local_addr = NULL; char *free_me0 = NULL; char *free_me1 = NULL; char *free_me2 = NULL; if (verbose || !(option_mask32 & OPT_E)) { if (!max_per_host) /* remote_addr is not yet known */ free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); if (option_mask32 & OPT_h) { free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); if (!remote_hostname) { bb_error_msg("cannot look up hostname for %s", remote_addr); remote_hostname = remote_addr; } } /* Find out local IP peer connected to. * Errors ignored (I'm not paranoid enough to imagine kernel * which doesn't know local IP). */ if (tcp) getsockname(0, &local.u.sa, &local.len); /* else: for UDP it is done earlier by parent */ local_addr = xmalloc_sockaddr2dotted(&local.u.sa); if (option_mask32 & OPT_h) { local_hostname = preset_local_hostname; if (!local_hostname) { free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); if (!local_hostname) bb_error_msg_and_die("cannot look up hostname for %s", local_addr); } /* else: local_hostname is not NULL, but is NOT malloced! */ } } if (verbose) { pid = getpid(); if (max_per_host) { bb_error_msg("concurrency %s %u/%u", remote_addr, cur_per_host, max_per_host); } bb_error_msg((option_mask32 & OPT_h) ? "start %u %s-%s (%s-%s)" : "start %u %s-%s", pid, local_addr, remote_addr, local_hostname, remote_hostname); } if (!(option_mask32 & OPT_E)) { /* setup ucspi env */ const char *proto = tcp ? "TCP" : "UDP"; /* Extract "original" destination addr:port * from Linux firewall. Useful when you redirect * an outbond connection to local handler, and it needs * to know where it originally tried to connect */ if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) { char *addr = xmalloc_sockaddr2dotted(&local.u.sa); xsetenv_plain("TCPORIGDSTADDR", addr); free(addr); } xsetenv_plain("PROTO", proto); xsetenv_proto(proto, "LOCALADDR", local_addr); xsetenv_proto(proto, "REMOTEADDR", remote_addr); if (option_mask32 & OPT_h) { xsetenv_proto(proto, "LOCALHOST", local_hostname); xsetenv_proto(proto, "REMOTEHOST", remote_hostname); } //compat? xsetenv_proto(proto, "REMOTEINFO", ""); /* additional */ if (cur_per_host > 0) /* can not be true for udp */ xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host)); } free(local_addr); free(free_me0); free(free_me1); free(free_me2); } xdup2(0, 1); signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); signal(SIGCHLD, SIG_DFL); sig_unblock(SIGCHLD); #ifdef SSLSVD strcpy(id, utoa(pid)); ssl_io(0, argv); #else BB_EXECVP(argv[0], argv); #endif bb_perror_msg_and_die("exec '%s'", argv[0]); }
int telnet_main(int argc UNUSED_PARAM, char **argv) { char *host; int port; int len; struct pollfd ufds[2]; INIT_G(); #if ENABLE_FEATURE_AUTOWIDTH get_terminal_width_height(0, &G.win_width, &G.win_height); #endif #if ENABLE_FEATURE_TELNET_TTYPE G.ttype = getenv("TERM"); #endif if (tcgetattr(0, &G.termios_def) >= 0) { G.do_termios = 1; G.termios_raw = G.termios_def; cfmakeraw(&G.termios_raw); } #if ENABLE_FEATURE_TELNET_AUTOLOGIN if (1 & getopt32(argv, "al:", &G.autologin)) G.autologin = getenv("USER"); argv += optind; #else argv++; #endif if (!*argv) bb_show_usage(); host = *argv++; port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23); if (*argv) /* extra params?? */ bb_show_usage(); xmove_fd(create_and_connect_stream_or_die(host, port), netfd); setsockopt_keepalive(netfd); signal(SIGINT, record_signo); ufds[0].fd = STDIN_FILENO; ufds[0].events = POLLIN; ufds[1].fd = netfd; ufds[1].events = POLLIN; while (1) { if (poll(ufds, 2, -1) < 0) { /* error, ignore and/or log something, bay go to loop */ if (bb_got_signal) con_escape(); else sleep(1); continue; } // FIXME: reads can block. Need full bidirectional buffering. if (ufds[0].revents) { len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE); if (len <= 0) doexit(EXIT_SUCCESS); TRACE(0, ("Read con: %d\n", len)); handle_net_output(len); } if (ufds[1].revents) { len = safe_read(netfd, G.buf, DATABUFSIZE); if (len <= 0) { full_write1_str("Connection closed by foreign host\r\n"); doexit(EXIT_FAILURE); } TRACE(0, ("Read netfd (%d): %d\n", netfd, len)); handle_net_input(len); } } /* while (1) */ }