void client_respond(int code, char *file, char *fmt, ...) { va_list aptr; char str[MAX_PATH_SIZE * 2], *p, *q; FILE *fp; /* ** Display additional info from file if found */ if (file != NULL && (fp = fopen(file, "r")) != NULL) { while (fgets(str, sizeof(str), fp) != NULL) { p = socket_msgline(str); if ((q = strchr(p, '\n')) != NULL) *q = '\0'; socket_printf(ctx.cli_ctrl, "%03d-%s\r\n", code, p); } fclose(fp); } /* ** The last line carries the ultimate reponse code */ va_start(aptr, fmt); #if defined(HAVE_VSNPRINTF) vsnprintf(str, sizeof(str), fmt, aptr); #else vsprintf(str, fmt, aptr); #endif va_end(aptr); socket_printf(ctx.cli_ctrl, "%03d %s.\r\n", code, str); }
void abort_handler(int signal) { static char crashed = FALSE; if (crashed) { exit(-1); } crashed = TRUE; restore_terminal(); clean_screen(gtd->ses); dump_stack(); fflush(NULL); exit(-1); if (gtd->ses->connect_retry > utime()) { gtd->ses->connect_retry = 0; } else if (HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_SGA) && !HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_ECHO)) { socket_printf(gtd->ses, 1, "%c", 3); } else { do_zap(gtd->ses, ""); } }
void interrupt_handler(int signal) { if (gtd->ses->connect_retry > utime()) { gtd->ses->connect_retry = 0; } else if (HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_SGA) && !HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_ECHO)) { socket_printf(gtd->ses, 1, "%c", 4); } else { cursor_delete_or_exit(gtd->ses, ""); } }
void read_key(void) { char buffer[BUFFER_SIZE]; struct listnode *node; struct listroot *root; int len, cnt, match; if (gtd->input_buf[0] == gtd->tintin_char) { read_line(); return; } len = read(0, buffer, 1); buffer[len] = 0; if (HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA) || HAS_BIT(gtd->flags, TINTIN_FLAG_CONVERTMETACHAR)) { convert_meta(buffer, >d->macro_buf[strlen(gtd->macro_buf)]); } else { strcat(gtd->macro_buf, buffer); } if (!HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA)) { match = 0; root = gtd->ses->list[LIST_MACRO]; for (root->update = 0; root->update < root->used; root->update++) { node = root->list[root->update]; if (!strcmp(gtd->macro_buf, node->pr)) { script_driver(gtd->ses, LIST_MACRO, node->right); gtd->macro_buf[0] = 0; return; } else if (!strncmp(gtd->macro_buf, node->pr, strlen(gtd->macro_buf))) { match = 1; } } if (match) { return; } } for (cnt = 0; gtd->macro_buf[cnt]; cnt++) { switch (gtd->macro_buf[cnt]) { case '\n': gtd->input_buf[0] = 0; gtd->macro_buf[0] = 0; gtd->input_len = 0; if (HAS_BIT(gtd->ses->flags, SES_FLAG_RUN)) { socket_printf(gtd->ses, 1, "%c", '\r'); } else { socket_printf(gtd->ses, 2, "%c%c", '\r', '\n'); } break; default: if (gtd->macro_buf[cnt] == gtd->tintin_char && gtd->input_buf[0] == 0) { if (gtd->input_len != gtd->input_cur) { printf("\033[1@%c", gtd->macro_buf[cnt]); } else { printf("%c", gtd->macro_buf[cnt]); } gtd->input_buf[0] = gtd->tintin_char; gtd->input_buf[1] = 0; gtd->macro_buf[0] = 0; gtd->input_len = 1; gtd->input_cur = 1; gtd->input_pos = 1; } else { socket_printf(gtd->ses, 1, "%c", gtd->macro_buf[cnt]); gtd->input_buf[0] = 127; gtd->macro_buf[0] = 0; gtd->input_len = 0; } break; } } }
static void client_xfer_fireup(void) { u_int32_t ladr = INADDR_ANY; int incr; /* ** should we bind a rand(port-range) or increment? */ incr = !config_bool(NULL,"SockBindRand", 0); /* ** If appropriate, connect to the client's data port */ if (ctx.cli_mode == MOD_ACT_FTP) { /* ** TransProxy mode: check if we can use our real ** ip instead of the server's one as our local ip, ** we pre-bind the socket/ports to before connect. */ if(config_bool(NULL, "AllowTransProxy", 0)) { ladr = config_addr(NULL, "Listen", (u_int32_t)INADDR_ANY); } if(INADDR_ANY == ladr) { ladr = socket_sck2addr(ctx.cli_ctrl->sock, LOC_END, NULL); } if (socket_d_connect(ctx.cli_addr, ctx.cli_port, ladr, ctx.act_lrng, ctx.act_urng, &(ctx.cli_data), "Cli-Data", incr) == 0) { syslog_error("can't connect Cli-Data for %s", ctx.cli_ctrl->peer); client_respond(425, NULL, "Can't open data connection"); client_data_reset(MOD_RESET); ctx.expect = EXP_IDLE; return; } } /* ** Send the original command from the client */ if (ctx.xfer_arg[0] != '\0') { socket_printf(ctx.srv_ctrl, "%s %s\r\n", ctx.xfer_cmd, ctx.xfer_arg); syslog_write(T_INF, "'%s %s' sent for %s", ctx.xfer_cmd, ctx.xfer_arg, ctx.cli_ctrl->peer); } else { socket_printf(ctx.srv_ctrl, "%s\r\n", ctx.xfer_cmd); syslog_write(T_INF, "'%s' sent for %s", ctx.xfer_cmd, ctx.cli_ctrl->peer); } /* ** Prepare the handling and statistics buffers */ memset(ctx.xfer_rep, 0, sizeof(ctx.xfer_rep)); ctx.xfer_beg = time(NULL); ctx.expect = EXP_XFER; /* Expect 226 complete */ }
static void client_srv_ctrl_read(char *str) { int code, c1, c2, c3; char *arg; if (str == NULL) /* Basic sanity check */ return; syslog_write(T_DBG, "from Server-PI (%d): '%.512s'", ctx.srv_ctrl->sock, str); #if defined(COMPILE_DEBUG) debug(1, "from Server-PI (%d): '%.512s'", ctx.srv_ctrl->sock, str); #endif /* ** Intermediate responses can usually be forwarded */ if (*str < '2' || *str > '5' || str[3] != ' ') { /* ** If this is the destination host's ** welcome message let's discard it. */ if (ctx.expect == EXP_CONN) return; if (ctx.expect == EXP_USER && UAUTH_NONE != ctx.auth_mode) return; #if defined(COMPILE_DEBUG) debug(2, "'%.4s'... forwarded to %s %d=%s", str, ctx.cli_ctrl->ctyp, ctx.cli_ctrl->sock, ctx.cli_ctrl->peer); #endif socket_printf(ctx.cli_ctrl, "%s\r\n", str); return; } /* ** Consider only valid final response codes */ if ((code = atoi(str)) < 200 || code > 599) { syslog_error("bad response %d from server for %s", code, ctx.srv_ctrl->peer); return; } c1 = code / 100; c2 = (code % 100) / 10; c3 = code % 10; for (arg = str + 3; *arg == ' '; arg++) ; /* ** We have a response code, go see what we expected */ switch (ctx.expect) { case EXP_CONN: /* ** Waiting for a 220 Welcome */ if (c1 == 2) { socket_printf(ctx.srv_ctrl, "USER %s\r\n", ctx.username); ctx.expect = EXP_USER; } else { if(UAUTH_NONE != ctx.auth_mode) { client_respond(530, NULL, "Login incorrect"); } else { socket_printf(ctx.cli_ctrl, "%s\r\n", str); } ctx.expect = EXP_IDLE; ctx.cli_ctrl->kill = 1; } break; case EXP_USER: /* ** Only the following codes are useful: ** 230=logged in, ** 331=need password, ** 332=need password+account */ if(UAUTH_NONE != ctx.auth_mode) { /* ** logged in, NO password needed */ if(c1 == 2 && c2 == 3) { client_respond(230, NULL, "User logged in, proceed."); ctx.expect = EXP_IDLE; break; } else /* ** OK, password (+account) needed */ if(c1 == 3 && c2 == 3) { if(ctx.userpass) { socket_printf(ctx.srv_ctrl, "PASS %s\r\n", ctx.userpass); misc_free(FL, ctx.userpass); ctx.userpass = NULL; } else { socket_printf(ctx.srv_ctrl, "PASS \r\n"); } ctx.expect = EXP_PTHR; break; } } /* ** pass server response through to client */ socket_printf(ctx.cli_ctrl, "%s\r\n", str); if (c1 != 2 && c1 != 3) { ctx.cli_ctrl->kill = 1; } ctx.expect = EXP_IDLE; break; case EXP_ABOR: if (c1 == 2) { client_data_reset(MOD_RESET); ctx.expect = EXP_IDLE; } break; case EXP_PASV: if (code == 227 && *arg != '\0') { client_srv_passive(arg); } else { socket_printf(ctx.cli_ctrl, "%s\r\n", str); client_data_reset(MOD_RESET); ctx.expect = EXP_IDLE; } break; case EXP_PORT: if (code == 200) { client_xfer_fireup(); } else { socket_printf(ctx.cli_ctrl, "%s\r\n", str); client_data_reset(MOD_RESET); ctx.expect = EXP_IDLE; } break; case EXP_XFER: /* ** Distinguish between success and failure */ if (c1 == 2) { misc_strncpy(ctx.xfer_rep, str, sizeof(ctx.xfer_rep)); } else { socket_printf(ctx.cli_ctrl, "%s\r\n", str); if(config_bool(NULL,"FailResetsPasv", 0)) { client_data_reset(MOD_RESET); } else { client_data_reset(ctx.cli_mode); } } ctx.expect = EXP_IDLE; break; case EXP_PTHR: socket_printf(ctx.cli_ctrl, "%s\r\n", str); ctx.expect = EXP_IDLE; break; case EXP_IDLE: socket_printf(ctx.cli_ctrl, "%s\r\n", str); if (code == 421) { syslog_write(T_WRN, "server closed connection " "for %s", ctx.cli_ctrl->peer); ctx.cli_ctrl->kill = 1; } else { syslog_write(T_WRN, "bogus '%.512s' from " "Server-PI for %s", ctx.cli_ctrl->peer, str); } break; } }
static void client_cli_ctrl_read(char *str) { char *arg; CMD *cmd; int c; if (str == NULL) { /* Basic sanity check */ #if defined(COMPILE_DEBUG) debug(2, "null User-PI msg: nothing to do"); #endif return; } /* ** Handle a minimum amount of Telnet line control */ while ((arg = strchr(str, IAC)) != NULL) { c = (arg[1] & 255); switch (c) { case WILL: case WONT: /* ** RFC 1123, 4.1.2.12 */ syslog_write(U_WRN, "WILL/WONT refused for %s", ctx.cli_ctrl->peer); socket_printf(ctx.cli_ctrl, "%c%c%c", IAC, DONT, arg[2]); if(arg[2]) memmove(arg, arg + 3, strlen(arg) - 2); else memmove(arg, arg + 1, strlen(arg)); break; case DO: case DONT: /* ** RFC 1123, 4.1.2.12 */ syslog_write(U_WRN, "DO/DONT refused for %s", ctx.cli_ctrl->peer); socket_printf(ctx.cli_ctrl, "%c%c%c", IAC, WONT, arg[2]); if(arg[2]) memmove(arg, arg + 3, strlen(arg) - 2); else memmove(arg, arg + 1, strlen(arg)); break; case IAC: memmove(arg, arg + 1, strlen(arg)); break; case IP: case DM: syslog_write(U_INF, "IAC-%s from %s", (c == IP) ? "IP" : "DM", ctx.cli_ctrl->peer); memmove(arg, arg + 2, strlen(arg) - 1); break; default: memmove(arg, arg + 1, strlen(arg)); } } /* ** If there is nothing left to process, please call again */ if (str[0] == '\0') { #if defined(COMPILE_DEBUG) debug(2, "empty User-PI msg: nothing to do"); #endif return; } /* ** Separate arguments if given */ if ((arg = strchr(str, ' ')) == NULL) arg = strchr(str, '\t'); if (arg == NULL) arg = ""; else { while (*arg == ' ' || *arg == '\t') *arg++ = '\0'; } #if defined(COMPILE_DEBUG) debug(1, "from User-PI (%d): cmd='%.32s' arg='%.512s'", ctx.cli_ctrl->sock, str, NIL(arg)); #endif /* ** Try to execute the given command. The "USER" command ** must be enabled in any case, since it's the one to ** setup allow/deny (let's call it bootstrapping) ... */ for (cmd = cmds_get_list(); cmd->name != NULL; cmd++) { if (strcasecmp("USER", cmd->name) == 0) cmd->legal = 1; /* Need this one! */ if (strcasecmp(str, cmd->name) != 0) continue; if ((cmd->legal == 0) && strcasecmp("QUIT", cmd->name)) { client_respond(502, NULL, "'%.32s': " "command not implemented", str); syslog_write(U_WRN, "'%.32s' from %s not allowed", str, ctx.cli_ctrl->peer); return; } #if defined(HAVE_REGEX) if (cmd->regex != NULL) { char *p; p = cmds_reg_exec(cmd->regex, arg); if (p != NULL) { client_respond(501, NULL, "'%.32s': syntax error " "in arguments", str); syslog_write(U_WRN, "bad arg '%.128s'%s for " "'%s' from %s: %s", arg, (strlen(arg) > 128) ? "..." : "", cmd->name, ctx.cli_ctrl->peer, p); return; } } #endif ctx.curr_cmd = str; (*cmd->func)(&ctx, arg); return; } /* ** Arriving here means the command was not found... */ client_respond(500, NULL, "'%.32s': command unrecognized", str); syslog_write(U_WRN, "unknown '%.32s' from %s", str, ctx.cli_ctrl->peer); }
void client_run(void) { int sock, need, diff; char str[MAX_PATH_SIZE * 2]; char *p, *q; FILE *fp; BUF *buf; /* ** Setup client signal handling (mostly graceful exit) */ signal(SIGINT, client_signal); signal(SIGTERM, client_signal); signal(SIGQUIT, client_signal); signal(SIGHUP, client_signal); signal(SIGCHLD, SIG_IGN); signal(SIGUSR1, SIG_IGN); /* ** Prepare our general client context */ memset(&ctx, 0, sizeof(ctx)); ctx.sess_beg = time(NULL); ctx.cli_mode = MOD_ACT_FTP; ctx.expect = EXP_IDLE; ctx.timeout = config_int(NULL, "TimeOut", 900); sock = fileno(stdin); /* "recover" our socket */ /* ** Check whether a DenyMessage file exists. This ** indicates that we are currently not willing ** to serve any clients. */ p = config_str(NULL, "DenyMessage", NULL); if (p != NULL && (fp = fopen(p, "r")) != NULL) { while (fgets(str, sizeof(str) - 4, fp) != NULL) { p = socket_msgline(str); if ((q = strchr(p, '\n')) != NULL) strcpy(q, "\r\n"); else strcat(p, "\r\n"); send(sock, "421-", 4, 0); send(sock, p, strlen(p), 0); } fclose(fp); if ((p = config_str(NULL, "DenyString", NULL)) != NULL) p = socket_msgline(p); else p = "Service not available"; send(sock, "421 ", 4, 0); send(sock, p, strlen(p), 0); send(sock, ".\r\n", 3, 0); p = socket_addr2str(socket_sck2addr(sock, REM_END, NULL)); close(sock); syslog_write(U_ERR, "reject: '%s' (DenyMessage)", p); exit(EXIT_SUCCESS); } /* ** Create a High Level Socket for the client's User-PI */ if ((ctx.cli_ctrl = socket_init(sock)) == NULL) misc_die(FL, "client_run: ?cli_ctrl?"); ctx.cli_ctrl->ctyp = "Cli-Ctrl"; /* ** Announce the connection request */ syslog_write(U_INF, "connect from %s", ctx.cli_ctrl->peer); /* ** Display the welcome message (invite the user to login) */ if ((p = config_str(NULL, "WelcomeString", NULL)) == NULL) p = "%h FTP server (Version %v - %b) ready"; misc_strncpy(str, socket_msgline(p), sizeof(str)); client_respond(220, config_str(NULL, "WelcomeMessage", NULL), str); /* ** Enter the client mainloop */ while (close_flag == 0) { /* ** We need to go into select() only ** if all input has been processed ** or ** we wait for more data to get a line ** complete (partially sent, no EOL). ** ** (data buffers are never splited) */ need = 1; if (ctx.cli_ctrl && ctx.cli_ctrl->rbuf) need = 0; if (ctx.srv_ctrl && ctx.srv_ctrl->rbuf) need = 0; if((ctx.cli_ctrl && ctx.cli_ctrl->more>0) || (ctx.srv_ctrl && ctx.srv_ctrl->more>0)) need = 1; /* ** use higher priority to writes; ** read only if nothing to write... */ if(ctx.srv_data && ctx.cli_data) { if(ctx.srv_data->wbuf) { ctx.cli_data->more = -1; } else { ctx.cli_data->more = 0; } if(ctx.cli_data->wbuf) { ctx.srv_data->more = -1; } else { ctx.srv_data->more = 0; } } if (need != 0) { if (socket_exec(ctx.timeout, &close_flag) <= 0) break; /* Timed out or worse */ } #if defined(COMPILE_DEBUG) debug(4, "client-loop ..."); #endif /* ** Check if any zombie sockets can be removed */ if (ctx.cli_ctrl != NULL && ctx.cli_ctrl->sock == -1) close_flag = 1; /* Oops, forget it ... */ if (ctx.srv_ctrl != NULL && ctx.srv_ctrl->sock == -1) { #if defined(COMPILE_DEBUG) debug(3, "about to destroy Srv-Ctrl"); #endif /* ** If we have any open data connections, ** make really sure they don't survive. */ if (ctx.cli_data != NULL) ctx.cli_data->kill = 1; if (ctx.srv_data != NULL) ctx.srv_data->kill = 1; /* ** Our client should be informed */ if (ctx.cli_ctrl->kill == 0) { client_respond(421, NULL, "Service not available, " "closing control connection"); } /* ** Don't forget to remove the dead socket */ socket_kill(ctx.srv_ctrl); ctx.srv_ctrl = NULL; } if (ctx.cli_data != NULL && ctx.cli_data->sock == -1) { #if defined(COMPILE_DEBUG) debug(3, "about to destroy Cli-Data"); #endif /* ** If we have an outstanding server reply ** (e.g. 226 Transfer complete), send it. */ if (ctx.xfer_rep[0] != '\0') { socket_printf(ctx.cli_ctrl, "%s\r\n", ctx.xfer_rep); memset(ctx.xfer_rep, 0, sizeof(ctx.xfer_rep)); } else { if(ctx.expect == EXP_XFER) ctx.expect = EXP_PTHR; } /* ** Good time for statistics and data reset */ if (ctx.xfer_beg == 0) ctx.xfer_beg = time(NULL); diff = (int) (time(NULL) - ctx.xfer_beg); if (diff < 1) diff = 1; /* ** print our current statistic */ syslog_write(U_INF, "Transfer for %s %s: %s '%s' %s %u/%d byte/sec", ctx.cli_ctrl->peer, ctx.cli_data->ernr ? "failed" : "completed", ctx.xfer_cmd, ctx.xfer_arg, ctx.cli_data->rcnt ? "sent" : "read", ctx.cli_data->rcnt ? ctx.cli_data->rcnt : ctx.cli_data->wcnt, diff); /* ** update session statistics data */ if(ctx.cli_data->rcnt) ctx.xfer_rsec += diff; ctx.xfer_rcnt += ctx.cli_data->rcnt; if(ctx.cli_data->wcnt) ctx.xfer_wsec += diff; ctx.xfer_wcnt += ctx.cli_data->wcnt; /* ** reset data transfer state */ client_data_reset(MOD_RESET); /* ** Doom the corresponding server socket */ if (ctx.srv_data != NULL) ctx.srv_data->kill = 1; /* ** Don't forget to remove the dead socket */ socket_kill(ctx.cli_data); ctx.cli_data = NULL; } if (ctx.srv_data != NULL && ctx.srv_data->sock == -1) { #if defined(COMPILE_DEBUG) debug(3, "about to destroy Srv-Data"); #endif /* ** Doom the corresponding client socket if an ** error occured, FailResetsPasv=yes or we ** expect other response than PASV (Netscape!) */ if(ctx.cli_data != NULL) { if(0 != ctx.srv_data->ernr) { ctx.cli_data->ernr = -1; ctx.cli_data->kill = 1; } if(config_bool(NULL,"FailResetsPasv", 0)) { ctx.cli_data->kill = 1; } else if(ctx.expect != EXP_PASV) { ctx.cli_data->kill = 1; } } /* ** Don't forget to remove the dead socket */ socket_kill(ctx.srv_data); ctx.srv_data = NULL; } /* ** Serve the control connections */ if (ctx.cli_ctrl != NULL && ctx.cli_ctrl->rbuf != NULL) { if (socket_gets(ctx.cli_ctrl, str, sizeof(str)) != NULL) client_cli_ctrl_read(str); } if (ctx.srv_ctrl != NULL && ctx.srv_ctrl->rbuf != NULL) { if (socket_gets(ctx.srv_ctrl, str, sizeof(str)) != NULL) client_srv_ctrl_read(str); } /* ** Serve the data connections. This is a bit tricky, ** since all we do is move the buffer pointers. */ if (ctx.cli_data != NULL && ctx.srv_data != NULL) { if (ctx.cli_data->rbuf != NULL) { #if defined(COMPILE_DEBUG) debug(2, "Cli-Data -> Srv-Data"); #endif if (ctx.srv_data->wbuf == NULL) { ctx.srv_data->wbuf = ctx.cli_data->rbuf; } else { for (buf = ctx.srv_data->wbuf; buf && buf->next; buf = buf->next) ; buf->next = ctx.cli_data->rbuf; } ctx.cli_data->rbuf = NULL; } if (ctx.srv_data->rbuf != NULL) { #if defined(COMPILE_DEBUG) debug(2, "Srv-Data -> Cli-Data"); #endif if (ctx.cli_data->wbuf == NULL) { ctx.cli_data->wbuf = ctx.srv_data->rbuf; } else { for (buf = ctx.cli_data->wbuf; buf && buf->next; buf = buf->next) ; buf->next = ctx.srv_data->rbuf; } ctx.srv_data->rbuf = NULL; } } /* at this point the main loop resumes ... */ } /* ** Display basic session statistics... ** in secs since session begin ** downloads / read (xfer-reads from server) ** uploads / send (xfer-sends from server) */ syslog_write(U_INF, "closing connect from %s after %d secs - " "read %d/%d, sent %d/%d byte/sec", ctx.cli_ctrl ? ctx.cli_ctrl->peer : "unknown peer", time(NULL)-ctx.sess_beg, ctx.xfer_wcnt, ctx.xfer_wsec, ctx.xfer_rcnt, ctx.xfer_rsec); /* ** Free allocated memory */ ctx.magic_auth = NULL; if (ctx.userauth != NULL) { misc_free(FL, ctx.userauth); ctx.userauth = NULL; } if (ctx.username != NULL) { misc_free(FL, ctx.username); ctx.username = NULL; } if(ctx.userpass != NULL) { misc_free(FL, ctx.userpass); ctx.userpass = NULL; } #if defined(COMPILE_DEBUG) debug(1, "}}}}} %s client-exit", misc_getprog()); #endif exit(EXIT_SUCCESS); }