source: bootcd/isolinux/syslinux-6.03/core/fs/pxe/ftp.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 5.8 KB
Line 
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
31static 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
151static 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
163static 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
179static 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
185void 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
266err_disconnect:
267    core_tcp_write(ctlsock, "QUIT\r\n", 6, false);
268    core_tcp_close_file(inode);
269err_delete:
270    core_tcp_close_file(socket->ctl);
271err_free:
272    free_socket(socket->ctl);
273}
Note: See TracBrowser for help on using the repository browser.