• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
8  *   Boston MA 02110-1301, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12 
13 /*
14  * ftp.c
15  */
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <fcntl.h>
20 #include <minmax.h>
21 #include <sys/cpu.h>
22 #include <netinet/in.h>
23 #include <lwip/api.h>
24 #include "core.h"
25 #include "fs.h"
26 #include "pxe.h"
27 #include "thread.h"
28 #include "url.h"
29 #include "net.h"
30 
ftp_cmd_response(struct inode * inode,const char * cmd,const char * cmd_arg,uint8_t * pasv_data,int * pn_ptr)31 static int ftp_cmd_response(struct inode *inode, const char *cmd,
32 			    const char *cmd_arg,
33 			    uint8_t *pasv_data, int *pn_ptr)
34 {
35     struct pxe_pvt_inode *socket = PVT(inode);
36     int c;
37     int pos, code;
38     int pb, pn;
39     bool ps;
40     bool first_line, done;
41     char cmd_buf[4096];
42     int cmd_len;
43     const char *p;
44     char *q;
45     int err;
46 
47     if (cmd) {
48 	cmd_len = strlcpy(cmd_buf, cmd, sizeof cmd_buf);
49 	if (cmd_len >= sizeof cmd_buf - 3)
50 	    return -1;
51 	q = cmd_buf + cmd_len;
52 
53 	if (cmd_arg) {
54 	    p = cmd_arg;
55 
56 	    *q++ = ' ';
57 	    cmd_len++;
58 	    while (*p) {
59 		if (++cmd_len < sizeof cmd_buf) *q++ = *p;
60 		if (*p == '\r')
61 		    if (++cmd_len < sizeof cmd_buf) *q++ = '\0';
62 		p++;
63 	    }
64 
65 	    if (cmd_len >= sizeof cmd_buf - 2)
66 		return -1;
67 	}
68 
69 	*q++ = '\r';
70 	*q++ = '\n';
71 	cmd_len += 2;
72 
73 	err = core_tcp_write(socket, cmd_buf, cmd_len, true);
74 	if (err)
75 	    return -1;
76     }
77 
78     pos = code = pn = pb = 0;
79     ps = false;
80     first_line = true;
81     done = false;
82 
83     while ((c = pxe_getc(inode)) >= 0) {
84 	if (c == '\n') {
85 	    if (done) {
86 		if (pn) {
87 		    pn += ps;
88 		    if (pn_ptr)
89 			*pn_ptr = pn;
90 		}
91 		return code;
92 	    }
93 	    pos = code = 0;
94 	    first_line = false;
95 	    continue;
96 	}
97 
98 	switch (pos++) {
99 	case 0:
100 	case 1:
101 	case 2:
102 	    if (c < '0' || c > '9') {
103 		if (first_line)
104 		    return -1;
105 		else
106 		    pos = 4;	/* Skip this line */
107 	    } else {
108 		code = (code*10) + (c - '0');
109 	    }
110 	    break;
111 
112 	case 3:
113 	    pn = pb = 0;
114 	    ps = false;
115 	    if (c == ' ')
116 		done = true;
117 	    else if (c == '-')
118 		done = false;
119 	    else if (first_line)
120 		return -1;
121 	    else
122 		done = false;
123 	    break;
124 
125 	default:
126 	    if (pasv_data) {
127 		if (c >= '0' && c <= '9') {
128 		    pb = (pb*10) + (c-'0');
129 		    if (pn < 6)
130 			pasv_data[pn] = pb;
131 		    ps = true;
132 		} else if (c == ',') {
133 		    pn++;
134 		    pb = 0;
135 		    ps = false;
136 		} else if (pn) {
137 		    pn += ps;
138 		    if (pn_ptr)
139 			*pn_ptr = pn;
140 		    pn = pb = 0;
141 		    ps = false;
142 		}
143 	    }
144 	    break;
145 	}
146     }
147 
148     return -1;
149 }
150 
ftp_free(struct inode * inode)151 static void ftp_free(struct inode *inode)
152 {
153     struct pxe_pvt_inode *socket = PVT(inode);
154 
155     if (socket->ctl) {
156 	core_tcp_close_file(socket->ctl);
157 	free_socket(socket->ctl);
158 	socket->ctl = NULL;
159     }
160     core_tcp_close_file(inode);
161 }
162 
ftp_close_file(struct inode * inode)163 static void ftp_close_file(struct inode *inode)
164 {
165     struct pxe_pvt_inode *socket  = PVT(inode);
166     struct pxe_pvt_inode *ctlsock;
167     int resp;
168 
169     ctlsock = socket->ctl ? PVT(socket->ctl) : NULL;
170     if (core_tcp_is_connected(ctlsock)) {
171 	resp = ftp_cmd_response(socket->ctl, "QUIT", NULL, NULL, NULL);
172 	while (resp == 226) {
173 	    resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
174 	}
175     }
176     ftp_free(inode);
177 }
178 
179 static const struct pxe_conn_ops ftp_conn_ops = {
180     .fill_buffer	= core_tcp_fill_buffer,
181     .close		= ftp_close_file,
182     .readdir		= ftp_readdir,
183 };
184 
ftp_open(struct url_info * url,int flags,struct inode * inode,const char ** redir)185 void ftp_open(struct url_info *url, int flags, struct inode *inode,
186 	      const char **redir)
187 {
188     struct pxe_pvt_inode *socket = PVT(inode);
189     struct pxe_pvt_inode *ctlsock;
190     uint8_t pasv_data[6];
191     int pasv_bytes;
192     int resp;
193     err_t err;
194 
195     (void)redir;		/* FTP does not redirect */
196 
197     inode->size = 0;
198 
199     if (!url->port)
200 	url->port = 21;
201 
202     url_unescape(url->path, 0);
203 
204     socket->ops = &ftp_conn_ops;
205 
206     /* Set up the control connection */
207     socket->ctl = alloc_inode(inode->fs, 0, sizeof(struct pxe_pvt_inode));
208     if (!socket->ctl)
209 	return;
210     ctlsock = PVT(socket->ctl);
211     ctlsock->ops = &tcp_conn_ops; /* The control connection is just TCP */
212     if (core_tcp_open(ctlsock))
213 	goto err_free;
214     err = core_tcp_connect(ctlsock, url->ip, url->port);
215     if (err)
216 	goto err_delete;
217 
218     do {
219 	resp = ftp_cmd_response(socket->ctl, NULL, NULL, NULL, NULL);
220     } while (resp == 120);
221     if (resp != 220)
222 	goto err_disconnect;
223 
224     if (!url->user)
225 	url->user = "anonymous";
226     if (!url->passwd)
227 	url->passwd = "syslinux@";
228 
229     resp = ftp_cmd_response(socket->ctl, "USER", url->user, NULL, NULL);
230     if (resp != 202 && resp != 230) {
231 	if (resp != 331)
232 	    goto err_disconnect;
233 
234 	resp = ftp_cmd_response(socket->ctl, "PASS", url->passwd, NULL, NULL);
235 	if (resp != 230)
236 	    goto err_disconnect;
237     }
238 
239     if (!(flags & O_DIRECTORY)) {
240 	resp = ftp_cmd_response(socket->ctl, "TYPE", "I", NULL, NULL);
241 	if (resp != 200)
242 	    goto err_disconnect;
243     }
244 
245     resp = ftp_cmd_response(socket->ctl, "PASV", NULL, pasv_data, &pasv_bytes);
246     if (resp != 227 || pasv_bytes != 6)
247 	goto err_disconnect;
248 
249     err = core_tcp_open(socket);
250     if (err)
251 	goto err_disconnect;
252     err = core_tcp_connect(socket, *(uint32_t*)&pasv_data[0],
253 			   ntohs(*(uint16_t *)&pasv_data[4]));
254     if (err)
255 	goto err_disconnect;
256 
257     resp = ftp_cmd_response(socket->ctl,
258 			    (flags & O_DIRECTORY) ? "LIST" : "RETR",
259 			    url->path, NULL, NULL);
260     if (resp != 125 && resp != 150)
261 	goto err_disconnect;
262 
263     inode->size = -1;
264     return;			/* Sucess! */
265 
266 err_disconnect:
267     core_tcp_write(ctlsock, "QUIT\r\n", 6, false);
268     core_tcp_close_file(inode);
269 err_delete:
270     core_tcp_close_file(socket->ctl);
271 err_free:
272     free_socket(socket->ctl);
273 }
274