Esempio n. 1
0
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);
}
Esempio n. 2
0
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, "");
	}
}
Esempio n. 3
0
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, "");
	}
}
Esempio n. 4
0
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, &gtd->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;
    }
  }
}
Esempio n. 5
0
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	*/
}
Esempio n. 6
0
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;
	}
}
Esempio n. 7
0
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);
}
Esempio n. 8
0
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);
}