Home | History | Annotate | Download | only in pxe
      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 
     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 
    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 
    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 
    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