⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftp.c

📁 一个很有名的浏览器
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Internal "ftp" protocol implementation *//* $Id: ftp.c,v 1.182.2.7 2005/06/10 14:09:24 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdio.h>#include <ctype.h>#include <errno.h>#include <stdlib.h>#include <string.h>#include <sys/stat.h>	/* For converting permissions to strings */#include <sys/types.h>#ifdef HAVE_SYS_SOCKET_H#include <sys/socket.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#ifdef HAVE_FCNTL_H#include <fcntl.h> /* OS/2 needs this after sys/types.h */#endif/* We need to have it here. Stupid BSD. */#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#ifdef HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#include "elinks.h"#include "cache/cache.h"#include "config/options.h"#include "intl/gettext/libintl.h"#include "lowlevel/connect.h"#include "lowlevel/select.h"#include "modules/module.h"#include "osdep/osdep.h"#include "protocol/auth/auth.h"#include "protocol/ftp/ftp.h"#include "protocol/ftp/parse.h"#include "protocol/uri.h"#include "sched/connection.h"#include "util/conv.h"#include "util/error.h"#include "util/memory.h"#include "util/string.h"struct option_info ftp_options[] = {	INIT_OPT_TREE("protocol", N_("FTP"),		"ftp", 0,		N_("FTP specific options.")),	INIT_OPT_TREE("protocol.ftp", N_("Proxy configuration"),		"proxy", 0,		N_("FTP proxy configuration.")),	INIT_OPT_STRING("protocol.ftp.proxy", N_("Host and port-number"),		"host", 0, "",		N_("Host and port-number (host:port) of the FTP proxy, or blank.\n"		"If it's blank, FTP_PROXY environment variable is checked as well.")),	INIT_OPT_STRING("protocol.ftp", N_("Anonymous password"),		"anon_passwd", 0, "some@host.domain",		N_("FTP anonymous password to be sent.")),	INIT_OPT_BOOL("protocol.ftp", N_("Use passive mode (IPv4)"),		"use_pasv", 0, 1,		N_("Use PASV instead of PORT (passive vs active mode, IPv4 only).")),#ifdef CONFIG_IPV6	INIT_OPT_BOOL("protocol.ftp", N_("Use passive mode (IPv6)"),		"use_epsv", 0, 0,		N_("Use EPSV instead of EPRT (passive vs active mode, IPv6 only).")),#endif /* CONFIG_IPV6 */	NULL_OPTION_INFO,};struct module ftp_protocol_module = struct_module(	/* name: */		N_("FTP"),	/* options: */		ftp_options,	/* hooks: */		NULL,	/* submodules: */	NULL,	/* data: */		NULL,	/* init: */		NULL,	/* done: */		NULL);/* Constants */#define FTP_BUF_SIZE	16384/* Types and structs */struct ftp_connection_info {	int pending_commands;        /* Num of commands queued */	int opc;                     /* Total num of commands queued */	int conn_state;	int buf_pos;	unsigned int dir:1;          /* Directory listing in progress */	unsigned int rest_sent:1;    /* Sent RESTore command */	unsigned int has_data:1;     /* Do we have data socket? */	unsigned int use_pasv:1;     /* Use PASV (yes or no) */#ifdef CONFIG_IPV6	unsigned int use_epsv:1;     /* Use EPSV */#endif	unsigned char ftp_buffer[FTP_BUF_SIZE];	unsigned char cmd_buffer[1]; /* Must be last field !! */};/* Prototypes */static void ftp_login(struct connection *);static void ftp_send_retr_req(struct connection *, int);static void ftp_got_info(struct connection *, struct read_buffer *);static void ftp_got_user_info(struct connection *, struct read_buffer *);static void ftp_pass(struct connection *);static void ftp_pass_info(struct connection *, struct read_buffer *);static void ftp_retr_file(struct connection *, struct read_buffer *);static void ftp_got_final_response(struct connection *, struct read_buffer *);static void got_something_from_data_connection(struct connection *);static void ftp_end_request(struct connection *, int);static struct ftp_connection_info *add_file_cmd_to_str(struct connection *);/* Parse EPSV or PASV response for address and/or port. * int *n should point to a sizeof(int) * 6 space. * It returns zero on error or count of parsed numbers. * It returns an error if: * - there's more than 6 or less than 1 numbers. * - a number is strictly greater than max. * * On success, array of integers *n is filled with numbers starting * from end of array (ie. if we found one number, you can access it using * n[5]). * * Important: * Negative numbers aren't handled so -123 is taken as 123. * We don't take care about separators.*/static intparse_psv_resp(unsigned char *data, int *n, int max_value){	unsigned char *p = data;	int i = 5;	memset(n, 0, 6 * sizeof(*n));	if (*p < ' ') return 0;	/* Find the end. */	while (*p >= ' ') p++;	/* Ignore non-numeric ending chars. */       	while (p != data && !isdigit(*p)) p--;	if (p == data) return 0;	while (i >= 0) {		int x = 1;		/* Parse one number. */		while (p != data && isdigit(*p)) {			n[i] += (*p - '0') * x;			if (n[i] > max_value) return 0;			x *= 10;			p--;		}		/* Ignore non-numeric chars. */		while (p != data && !isdigit(*p)) p--;		if (p == data) return (6 - i);		/* Get the next one. */		i--;	}	return 0;}/* Returns 0 if there's no numeric response, -1 if error, the positive response * number otherwise. */static intget_ftp_response(struct connection *conn, struct read_buffer *rb, int part,		 struct sockaddr_storage *sa){	int pos;	set_connection_timeout(conn);again:	for (pos = 0; pos < rb->len; pos++) {		unsigned char *num_end;		int response;		if (rb->data[pos] != ASCII_LF) continue;		errno = 0;		response = strtoul(rb->data, (char **) &num_end, 10);		if (errno || num_end != rb->data + 3 || response < 100)			return -1;		if (sa && response == 227) { /* PASV response parsing. */			struct sockaddr_in *s = (struct sockaddr_in *) sa;			int n[6];			if (parse_psv_resp(num_end, (int *) &n, 255) != 6)				return -1;			memset(s, 0, sizeof(*s));			s->sin_family = AF_INET;			s->sin_addr.s_addr = htonl((n[0] << 24) + (n[1] << 16) + (n[2] << 8) + n[3]);			s->sin_port = htons((n[4] << 8) + n[5]);		}#ifdef CONFIG_IPV6		if (sa && response == 229) { /* EPSV response parsing. */			/* See RFC 2428 */			struct sockaddr_in6 *s = (struct sockaddr_in6 *) sa;			int sal = sizeof(*s);			int n[6];			if (parse_psv_resp(num_end, (int *) &n, 65535) != 1) {				return -1;			}			memset(s, 0, sizeof(*s));			if (getpeername(conn->socket.fd, (struct sockaddr *) sa, &sal)) {				return -1;			}			s->sin6_family = AF_INET6;			s->sin6_port = htons(n[5]);		}#endif		if (*num_end == '-') {			int i;			for (i = 0; i < rb->len - 5; i++)				if (rb->data[i] == ASCII_LF				    && !memcmp(rb->data+i+1, rb->data, 3)				    && rb->data[i+4] == ' ') {					for (i++; i < rb->len; i++)						if (rb->data[i] == ASCII_LF)							goto ok;					return 0;				}			return 0;ok:			pos = i;		}		if (!part && response >= 100 && response < 200) {			kill_buffer_data(rb, pos + 1);			goto again;		}		if (part == 2)			return response;		kill_buffer_data(rb, pos + 1);		return response;	}	return 0;}/* Initialize or continue ftp connection. */voidftp_protocol_handler(struct connection *conn){	set_connection_timeout(conn);	if (!has_keepalive_connection(conn)) {		make_connection(conn, &conn->socket, ftp_login);	} else {		ftp_send_retr_req(conn, S_SENT);	}}/* Get connection response. */static voidget_resp(struct connection *conn){	struct read_buffer *rb = alloc_read_buffer(conn);	if (!rb) return;	read_from_socket(conn, &conn->socket, rb, conn->read_func);}/* Send command, set connection state and free cmd string. */static voidsend_cmd(struct connection *conn, struct string *cmd, void *callback, int state){	conn->read_func = callback;	write_to_socket(conn, &conn->socket, cmd->source, cmd->length, get_resp);	done_string(cmd);	set_connection_state(conn, state);}/* Check if this auth token really belongs to this URI. */static intauth_user_matching_uri(struct auth_entry *auth, struct uri *uri){	if (!uri->userlen) /* Noone said it doesn't. */		return 1;	return !strlcasecmp(auth->user, -1, uri->user, uri->userlen);}/* Kill the current connection and ask for a username/password for the next * try. */static voidprompt_username_pw(struct connection *conn){	if (!conn->cached) {		conn->cached = get_cache_entry(conn->uri);		if (!conn->cached) {			abort_conn_with_state(conn, S_OUT_OF_MEM);			return;		}	}	mem_free_set(&conn->cached->content_type, stracpy("text/html"));	if (!conn->cached->content_type) {		abort_conn_with_state(conn, S_OUT_OF_MEM);		return;	}	add_auth_entry(conn->uri, "FTP Login", NULL, NULL, 0);	abort_conn_with_state(conn, S_OK);}/* Send USER command. */static voidftp_login(struct connection *conn){	struct string cmd;	struct auth_entry* auth;	auth = find_auth(conn->uri);	if (!init_string(&cmd)) {		abort_conn_with_state(conn, S_OUT_OF_MEM);		return;	}	add_to_string(&cmd, "USER ");	if (conn->uri->userlen) {		struct uri *uri = conn->uri;		add_bytes_to_string(&cmd, uri->user, uri->userlen);	} else if (auth && auth->valid) {		add_to_string(&cmd, auth->user);	} else {		add_to_string(&cmd, "anonymous");	}	add_crlf_to_string(&cmd);	send_cmd(conn, &cmd, (void *) ftp_got_info, S_SENT);}/* Parse connection response. */static voidftp_got_info(struct connection *conn, struct read_buffer *rb){	int response = get_ftp_response(conn, rb, 0, NULL);	if (response == -1) {		abort_conn_with_state(conn, S_FTP_ERROR);		return;	}	if (!response) {		read_from_socket(conn, &conn->socket, rb, ftp_got_info);		return;	}	/* RFC959 says that possible response codes on connection are:	 * 120 Service ready in nnn minutes.	 * 220 Service ready for new user.	 * 421 Service not available, closing control connection. */	if (response != 220) {		/* TODO? Retry in case of ... ?? */		retry_conn_with_state(conn, S_FTP_UNAVAIL);		return;	}	ftp_got_user_info(conn, rb);}/* Parse USER response and send PASS command if needed. */static voidftp_got_user_info(struct connection *conn, struct read_buffer *rb){	int response = get_ftp_response(conn, rb, 0, NULL);	if (response == -1) {		abort_conn_with_state(conn, S_FTP_ERROR);		return;	}	if (!response) {		read_from_socket(conn, &conn->socket, rb, ftp_got_user_info);		return;	}	/* RFC959 says that possible response codes for USER are:	 * 230 User logged in, proceed.	 * 331 User name okay, need password.	 * 332 Need account for login.	 * 421 Service not available, closing control connection.	 * 500 Syntax error, command unrecognized.	 * 501 Syntax error in parameters or arguments.	 * 530 Not logged in. */	/* FIXME? Since ACCT command isn't implemented, login to a ftp server	 * requiring it will fail (332). */	if (response == 332 || response >= 500) {		prompt_username_pw(conn);		return;	}	/* We don't require exact match here, as this is always error and some	 * non-RFC compliant servers may return even something other than 421.	 * --Zas */	if (response >= 400) {		abort_conn_with_state(conn, S_FTP_UNAVAIL);		return;	}	if (response == 230) {		ftp_send_retr_req(conn, S_GETH);		return;	}	ftp_pass(conn);}/* Send PASS command. */static voidftp_pass(struct connection *conn){	struct string cmd;	struct auth_entry *auth;	auth = find_auth(conn->uri);	if (!init_string(&cmd)) {		abort_conn_with_state(conn, S_OUT_OF_MEM);		return;	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -