summaryrefslogtreecommitdiff
path: root/polux/application/boottable/src/ftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'polux/application/boottable/src/ftp.c')
-rw-r--r--polux/application/boottable/src/ftp.c377
1 files changed, 377 insertions, 0 deletions
diff --git a/polux/application/boottable/src/ftp.c b/polux/application/boottable/src/ftp.c
new file mode 100644
index 0000000000..abd81714aa
--- /dev/null
+++ b/polux/application/boottable/src/ftp.c
@@ -0,0 +1,377 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ftpget
+ *
+ * Mini implementation of FTP to retrieve a remote file.
+ *
+ * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com>
+ * Copyright (C) 2002 Glenn McGrath <bug1@iinet.net.au>
+ *
+ * Based on wget.c by Chip Rosenthal Covad Communications
+ * <chip@laserlink.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include "image_table.h"
+#include "stdtypes.h"
+
+#include "syslog.h"
+
+#include "spidlib.h"
+
+#define FTP_PORT 21
+
+extern void bb_error_msg_and_die(const char *s, ...);
+
+static char verbose_flag = 0;
+
+static unsigned char min_ascii_table[] = {
+ ['0'] = 0x0, ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3,
+ ['4'] = 0x4, ['5'] = 0x5, ['6'] = 0x6, ['7'] = 0x7,
+ ['8'] = 0x8, ['9'] = 0x9, ['a'] = 0xa, ['b'] = 0xb,
+ ['c'] = 0xc, ['d'] = 0xd, ['e'] = 0xe, ['f'] = 0xf,
+ ['A'] = 0xa, ['B'] = 0xb, ['C'] = 0xc, ['D'] = 0xd,
+ ['E'] = 0xe, ['F'] = 0xf
+};
+
+#define IS_HEX_DIGIT(x) \
+ (x >= '0' && x <= '9') || \
+ (x >= 'A' && x <= 'F') || \
+ (x >= 'a' && x <= 'f')
+
+static void hex2mem(const char *hex, unsigned char *mem, size_t len){
+
+ int i, n;
+ for(i = 0, n = 0; i < len; i++, n = i*2){
+ /* Check n to avoid segfaults */
+ mem[i] = (IS_HEX_DIGIT(hex[n]) ? (min_ascii_table[(int)hex[n]] << 4) : 0);
+ mem[i] |= (IS_HEX_DIGIT(hex[n+1]) ? min_ascii_table[(int)hex[n+1]] : 0);
+ }
+}
+
+static int safe_strtoul(char *arg, unsigned long* value)
+{
+ char *endptr;
+ int errno_save = errno;
+
+ //assert(arg!=NULL);
+ errno = 0;
+ *value = strtoul(arg, &endptr, 0);
+ if (errno != 0 || *endptr!='\0' || endptr==arg) {
+ return 1;
+ }
+ errno = errno_save;
+ return 0;
+}
+
+static struct hostent *xgethostbyname(const char *name)
+{
+ struct hostent *retval;
+
+ if ((retval = gethostbyname(name)) == NULL)
+ bb_error_msg_and_die("%s", name);
+
+ return retval;
+}
+
+static void bb_lookup_host(struct sockaddr_in *s_in, const char *host)
+{
+ struct hostent *he;
+
+ memset(s_in, 0, sizeof(struct sockaddr_in));
+ s_in->sin_family = AF_INET;
+ he = xgethostbyname(host);
+ memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length);
+}
+
+static int xconnect_ftpdata(ftp_host_info_t *server, const char *buf)
+{
+ char *buf_ptr;
+ unsigned short port_num;
+
+ buf_ptr = strrchr(buf, ',');
+ *buf_ptr = '\0';
+ port_num = atoi(buf_ptr + 1);
+
+ buf_ptr = strrchr(buf, ',');
+ *buf_ptr = '\0';
+ port_num += atoi(buf_ptr + 1) * 256;
+
+ server->s_in->sin_port=htons(port_num);
+ return(xconnect(server->s_in));
+}
+
+static int ftp_secure_receive(ftp_host_info_t *server,
+ FILE *control_stream,
+ const char *server_path,
+ int fd_w,
+ unsigned char *md) {
+
+ md5_state_t c;
+ ulong len;
+ char md5[16];
+ char buf[512];
+ int ret = -1, fd_r;
+ unsigned char tmp[BUFSIZ];
+
+ /* Connect to the data socket */
+ if(ftpcmd("PASV", NULL, control_stream, buf) != 227)
+ {
+ syslog(LOG_WARNING, "PASV error: %s", buf + 4);
+ return -1;
+ }
+
+ fd_r = xconnect_ftpdata(server, buf);
+
+ if(ftpcmd("SIZE ", server_path, control_stream, buf) == 213){
+ if(safe_strtoul(buf + 4, &len))
+ {
+ syslog(LOG_WARNING, "SIZE error: %s", buf + 4);
+ return -1;
+ }
+
+ ret = len;
+ }
+
+ if(ftpcmd("RETR ", server_path, control_stream, buf) > 150)
+ {
+ syslog(LOG_WARNING, "RETR error: %s", buf + 4);
+ return -1;
+ }
+
+ spidlib_md5_init(&c);
+
+ /* Copy the file */
+ while((len = read(fd_r, tmp, sizeof(tmp))) > 0){
+ if(write(fd_w, tmp, len) < 0)
+ {
+ syslog(LOG_WARNING, "tmp file write error");
+ return -1;
+ }
+
+ /* Update md5 here */
+ spidlib_md5_append(&c,tmp, len);
+ }
+
+ if(len < 0)
+ {
+ syslog(LOG_WARNING, "ftp read error");
+ return -1;
+ }
+
+ /* len == 0 */
+ /* Check md5 here */
+ spidlib_md5_finish(&c, md5);
+ if(!!memcmp(md5, md, sizeof(md5)))
+ {
+ syslog(LOG_WARNING, "ftp md5 comparison error");
+ return -1;
+ }
+
+ /* Close */
+ close(fd_r);
+ if(ftpcmd(NULL, NULL, control_stream, buf) != 226)
+ {
+ syslog(LOG_WARNING, "ftp error: %s", buf + 4);
+ return -1;
+ }
+
+ ftpcmd("QUIT", NULL, control_stream, buf);
+
+ return ret;
+}
+
+static int ftp_secure_receive_md5(ftp_host_info_t *server,
+ FILE *control_stream,
+ const char *server_path,
+ unsigned char *md, spidlib_archi_t *archi_retrieved) {
+
+ char tmp[32];
+ char buf[512];
+ char tmp2[512];
+ char filename[512];
+ char archi[16];
+ int ret = -1, fd_r;
+
+ /* Connect to the data socket */
+ if(ftpcmd("PASV", NULL, control_stream, buf) != 227)
+ {
+ syslog(LOG_WARNING, "PASV error: %s", buf + 4);
+ return -1;
+ }
+
+ fd_r = xconnect_ftpdata(server, buf);
+
+ if(ftpcmd("SIZE ", server_path, control_stream, buf) == 213){
+ ulong len;
+ if(safe_strtoul(buf + 4, &len))
+ {
+ syslog(LOG_WARNING, "SIZE error: %s", buf + 4);
+ return -1;
+ }
+
+ ret = len;
+ }
+
+ if(ftpcmd("RETR ", server_path, control_stream, buf) > 150)
+ {
+ syslog(LOG_WARNING, "RETR error: %s", buf + 4);
+ return -1;
+ }
+
+ /* Get the file */
+ if(read(fd_r, tmp, sizeof(tmp)) < sizeof(tmp))
+ {
+ syslog(LOG_WARNING, "ftp md5 read error");
+ return -1;
+ }
+
+ if (read(fd_r, tmp2, sizeof(tmp2)) < 0)
+ {
+ syslog(LOG_WARNING, "ftp archi read error");
+ return -1;
+ }
+
+ /* Close */
+ close(fd_r);
+
+ /* Convert data */
+ hex2mem(tmp, md, 16);
+ sscanf(tmp2, "%s\n%s", filename, archi);
+
+ //syslog(LOG_WARNING, "what we got then? %s, %s", filename, archi);
+
+ if (strcmp(archi, "spc200c") == 0)
+ *archi_retrieved = SPIDLIB_ARCHI_SPC200C;
+ else if (strcmp(archi, "spc200e") == 0)
+ *archi_retrieved = SPIDLIB_ARCHI_SPC200E;
+ else
+ *archi_retrieved = SPIDLIB_ARCHI_UNKNOWN;
+
+ if(ftpcmd(NULL, NULL, control_stream, buf) != 226)
+ {
+ syslog(LOG_WARNING, "ftp error: %s", buf + 4);
+ return -1;
+ }
+
+ return ret;
+}
+
+int ftp_main(char *ftpIp, char* remoteFile, image_desc_t *image_desc, char * name)
+{
+ /* socket to ftp server */
+ FILE *control_stream;
+ struct sockaddr_in s_in;
+
+ /* continue a prev transfer (-c) */
+ ftp_host_info_t server;
+
+ /* Set default values */
+ server.user = "spidcom";
+ server.password = "spidcom";
+ verbose_flag = 0;
+
+ /* We want to do exactly _one_ DNS lookup, since some
+ * sites (i.e. ftp.us.debian.org) use round-robin DNS
+ * and we want to connect to only one IP... */
+ server.s_in = &s_in;
+ bb_lookup_host(&s_in, ftpIp);
+ s_in.sin_port = htons(FTP_PORT);
+
+ /* Connect/Setup/Configure the FTP session */
+ if(!(control_stream = ftp_login(&server)))
+ bb_error_msg_and_die("ftp server unavailable");
+
+ return(ftp_receive(&server, control_stream, remoteFile, image_desc, name));
+}
+
+int ftp_secure_main(const char *serverip,
+ const char* file,
+ const char *md5,
+ int fd,
+ unsigned char *md, char* login, char* password, unsigned int port) {
+
+ /* Socket to ftp server */
+ FILE *control_stream;
+ struct sockaddr_in s_in;
+
+ /* Continue a prev transfer (-c) */
+ ftp_host_info_t server;
+
+ /* Set default values */
+ if (login == NULL)
+ server.user = "spidcom";
+ else
+ server.user = login;
+ if (password == NULL)
+ server.password = "spidcom";
+ else
+ server.password = password;
+ verbose_flag = 0;
+
+ /* We want to do exactly _one_ DNS lookup, since some
+ * sites (i.e. ftp.us.debian.org) use round-robin DNS
+ * and we want to connect to only one IP... */
+ server.s_in = &s_in;
+ bb_lookup_host(&s_in, serverip);
+ if (port == 0)
+ s_in.sin_port = htons(FTP_PORT);
+ else
+ s_in.sin_port = htons(port);
+
+ /* Connect/Setup/Configure the FTP session */
+ if(!(control_stream = ftp_login(&server)))
+ {
+ syslog(LOG_WARNING, "ftp server unavailable");
+ return -1;
+ }
+
+ spidlib_archi_t archi_retrieved_md5;
+ spidlib_archi_t archi_retrieved_self;
+ ftp_secure_receive_md5(&server, control_stream, md5, md, &archi_retrieved_md5);
+
+ if (spidlib_get_architecture(&archi_retrieved_self) != 0)
+ archi_retrieved_self = SPIDLIB_ARCHI_UNKNOWN;
+
+ if ((archi_retrieved_self != archi_retrieved_md5) || archi_retrieved_self == SPIDLIB_ARCHI_UNKNOWN)
+ {
+ syslog(LOG_WARNING, "architectures not compatible");
+ return -1;
+ }
+
+ return(ftp_secure_receive(&server, control_stream, file, fd, md));
+}