changeset 420:68a672dd9d74

Enable HTTP support for upgrade utility.
author Aleksandr Rybalko <ray@ddteam.net>
date Fri, 14 Sep 2012 02:16:17 +0300
parents a245241eda54
children c685b3c5f40b
files target/sbin/upgrade/Makefile target/sbin/upgrade/geom_get_size.c target/sbin/upgrade/http_input.c target/sbin/upgrade/http_input.h target/sbin/upgrade/image.h target/sbin/upgrade/upgrade.c
diffstat 6 files changed, 1057 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/target/sbin/upgrade/Makefile	Fri Sep 14 02:13:58 2012 +0300
+++ b/target/sbin/upgrade/Makefile	Fri Sep 14 02:16:17 2012 +0300
@@ -1,10 +1,20 @@
 
-PROG=	upgrade
+PROG=		upgrade
+SRCS=		upgrade.c
+DPADD=		${LIBMD}
+LDADD=		-lmd -lutil
+BINDIR?=	/sbin/
+CFLAGS+=	-Wall -static
+NO_MAN=		yet
 
-#DPADD=	${LIBMD}
-#LDADD=	-lmd
-BINDIR?=/sbin/
-CFLAGS+=-Wall -static
-NO_MAN=	yet
+.if !defined(WITHOUT_HTTP_MODE)
+SRCS+=		http_input.c
+CFLAGS+=	-DWITH_HTTP_MODE
+.endif
+.if !defined(WITHOUT_GEOM_GET_SIZE)
+SRCS+=		geom_get_size.c
+CFLAGS+=	-DUSE_GEOM_GET_SIZE
+LDADD+=		-lgeom -lsbuf -lbsdxml
+.endif
 
 .include <bsd.prog.mk>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target/sbin/upgrade/geom_get_size.c	Fri Sep 14 02:16:17 2012 +0300
@@ -0,0 +1,41 @@
+
+#include <sys/types.h>
+#include <libgeom.h>
+
+size_t
+geom_get_size(char *gname)
+{
+	size_t sz;
+	int g;
+
+	g = g_open(gname, 0);
+	if (g == -1)
+		return (0);
+
+	sz = g_mediasize(g);
+	if (sz == -1)
+		return (0);
+
+	g_close(g);
+
+	return (sz);
+}
+
+#ifdef GEOM_GET_SIZE_TEST
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+	char * gname;
+
+	if (argc > 1)
+		gname = argv[1];
+	else
+		gname = "/dev/mirror/m0s1e";
+
+	size_t sz = geom_get_size(gname);
+
+	printf("%10zu\n", sz);
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target/sbin/upgrade/http_input.c	Fri Sep 14 02:16:17 2012 +0300
@@ -0,0 +1,676 @@
+/*-
+ * Copyright (c) 2012 Rybalko Aleksandr.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#define _WITH_DPRINTF
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "http_input.h"
+
+#define	CRLF	"\r\n"
+#define	RING_BUFFER_SIZE	256
+
+#ifdef	TARGET_VENDOR
+#define	PAGE_TITLE	TARGET_VENDOR TARGET_DEVICE "Firmware Update"
+#else
+#define	PAGE_TITLE	"D-Link DIR-632 Firmware Update"
+#endif
+
+#define	WRB_SIZE	1024
+int wrb_p = 0;
+char wrbuf[WRB_SIZE];
+
+struct rbuffer {
+	unsigned int 	curr;
+	unsigned int	total;
+	int		outfd;
+	int		matched;
+	int 		boundary_len;
+	unsigned int	filesize;		/* Collected file size */
+	char 		buf[RING_BUFFER_SIZE];
+	char 		*boundary;
+};
+
+static void
+http_templ(char *status_code, char *status, char *hdrs, char *title,
+    char *html)
+{
+
+	printf(
+	    "HTTP/1.1 %s %s" CRLF
+	    "Date: Thu, 19 Feb 2009 12:27:04 GMT" CRLF
+	    "Server: upgrade utility (ZRouter.org)" CRLF
+	    "Last-Modified: Wed, 18 Jun 2003 16:05:58 GMT" CRLF
+	    "Content-Type: text/html" CRLF
+	    "%s"
+	    "Connection: close" CRLF
+	    "" CRLF
+	    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
+		"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" CRLF
+	    "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" "
+		"lang=\"en\">" CRLF
+	    "  <head>" CRLF
+	    "    <title>%s</title>" CRLF
+	    "    <meta http-equiv=\"Content-Type\" content=\"text/html; "
+		"charset=utf-8\" />" CRLF
+	    "    <link rel=\"Help\" href=\"http://www.zrouter.org/\" />" CRLF
+	    "    <style type=\"text/css\">" CRLF
+	    "/*<![CDATA[*/" CRLF
+	    "BODY {" CRLF
+	    "    color: black;" CRLF
+	    "    background: white;" CRLF
+	    "}" CRLF
+	    "" CRLF
+	    "P.error {" CRLF
+	    "    color: red;" CRLF
+	    "}" CRLF
+	    "" CRLF
+	    "/*]]>*/" CRLF
+	    "    </style>" CRLF
+	    "  </head>" CRLF
+	    "  <body>" CRLF
+	    "    <center>" CRLF
+	    "%s" CRLF
+	    "    </center>" CRLF
+	    "  </body>" CRLF
+	    "</html>" CRLF,
+	    status_code, status, hdrs, title, html);
+}
+
+static void
+http_index()
+{
+
+	http_templ("200", "OK", "", PAGE_TITLE,
+	    "<h1>" PAGE_TITLE "</h1>" CRLF
+	    "<p>Select file with Firmware image.</p>" CRLF
+	    "<form method=\"POST\" enctype=\"multipart/form-data\">" CRLF
+	    "  <input type=\"file\" name=\"firmware\">" CRLF
+	    "  <input type=\"submit\">" CRLF
+	    "</form>" CRLF);
+}
+
+static void
+http_ok()
+{
+
+	http_templ("200", "OK", "", PAGE_TITLE,
+	    "<h1>" PAGE_TITLE "</h1>" CRLF
+	    "<p>Firmware image transferred successfully.</p>" CRLF
+	    "<p>Update procedure started.</p>" CRLF);
+}
+
+static void
+http_unauth(struct args *args)
+{
+	char WWW_Auth[256];
+
+	snprintf(WWW_Auth, 255,
+	    "WWW-Authenticate: Basic realm=\"%s\"" CRLF, args->realm);
+	WWW_Auth[255] = '\0';
+
+	http_templ("401", "Unauthorized",
+	    WWW_Auth,
+	    PAGE_TITLE,
+	    "<h1>" PAGE_TITLE "</h1>" CRLF
+	    "<p>Authorization required.</p>" CRLF
+	    "<p>Specify correct username and password.</p>" CRLF);
+}
+
+static void
+http_fail()
+{
+
+	http_templ("200", "OK", "", PAGE_TITLE,
+	    "<h1>" PAGE_TITLE "</h1>" CRLF
+	    "<p>Firmware image transferr failed.</p>" CRLF
+	    "<p>Please try again.</p>" CRLF);
+}
+
+static void
+http_error(char *msg)
+{
+
+	http_templ("200", "OK", "", PAGE_TITLE, msg);
+}
+
+static int 
+local_write(int fd, char *buf, int count)
+{
+
+	if (count > 1) {
+		if (wrb_p > 0) {
+			/* Send buffered data */
+			write(fd, wrbuf, wrb_p);
+			wrb_p = 0;
+		}
+		return (write(fd, buf, count));
+	}
+
+	if (wrb_p == WRB_SIZE) {
+		/* Buffer full, so write data */
+		write(fd, wrbuf, wrb_p);
+		wrb_p = 0;
+	}
+
+	wrbuf[wrb_p++] = buf[0];
+	return (1);
+
+}
+
+static int 
+local_close(int fd)
+{
+
+	write(fd, wrbuf, wrb_p);
+	wrb_p = 0;
+
+	return (close(fd));
+}
+
+static struct rbuffer *
+ring_buffer_init(char *boundary, int outfd)
+{
+	struct rbuffer *rb;
+
+	rb = (struct rbuffer *)malloc(sizeof(struct rbuffer));
+	if (!rb)
+		return (NULL);
+
+	rb->curr = rb->total = rb->matched = rb->filesize = 0;
+	bzero(rb->buf, RING_BUFFER_SIZE);
+	rb->outfd = outfd;
+
+	rb->boundary_len = strlen(boundary) + 2;
+	rb->boundary = calloc(rb->boundary_len + 1, 1);
+	if (!rb->boundary) {
+		free(rb);
+		return (NULL);
+	}
+	rb->boundary[0] = '-';
+	rb->boundary[1] = '-';
+	memcpy(rb->boundary + 2, boundary, rb->boundary_len - 2);
+
+	return (rb);
+}
+
+static void
+ring_buffer_free(struct rbuffer *rb)
+{
+
+	free(rb->boundary);
+	free(rb);
+}
+
+static int
+ring_buffer_append(struct rbuffer *rb, char ch)
+{
+
+	rb->curr = (rb->curr + 1) % RING_BUFFER_SIZE;
+	if (rb->total >= RING_BUFFER_SIZE) {
+		rb->filesize ++;
+		local_write(rb->outfd, &rb->buf[rb->curr], 1);
+	}
+	rb->buf[rb->curr] = ch;
+	rb->total++;
+
+	if (ch == rb->boundary[rb->matched])
+		rb->matched++;
+	else
+		rb->matched = 0;
+
+	if (rb->matched == rb->boundary_len)
+		return (1);
+
+	return (0);
+}
+
+static int
+ring_buffer_get_filesize(struct rbuffer *rb)
+{
+
+	return (rb->filesize);
+}
+
+static void
+ring_buffer_drain(struct rbuffer *rb)
+{
+	int i, last;
+
+	last = (rb->curr + RING_BUFFER_SIZE - rb->boundary_len) %
+	    RING_BUFFER_SIZE;
+	if (rb->buf[(last + RING_BUFFER_SIZE - 1) % RING_BUFFER_SIZE] == '\r')
+		last = (last + RING_BUFFER_SIZE - 1) % RING_BUFFER_SIZE;
+
+	if (rb->total < RING_BUFFER_SIZE) {
+		rb->filesize += rb->total;
+		local_write(rb->outfd, rb->buf+1, rb->total);
+	} else {
+		for (i = ((rb->curr + 1) % RING_BUFFER_SIZE); i != last;
+		    i = ((i + 1) % RING_BUFFER_SIZE)) {
+			rb->filesize ++;
+			local_write(rb->outfd, &rb->buf[i], 1);
+		}
+	}
+}
+
+static void
+skip_spaces(char **s)
+{
+
+	while ((**s != '\0') && isspace(**s))
+		(*s)++;
+}
+
+static int 
+local_read(struct args *args, char *buf, int count)
+{
+	int ret, m;
+
+	if (!args->enable_counter || (count > 1)) {
+		ret = read(args->fd, buf, count);
+	} else {
+		/* XXX: Can operate only if read always 1 byte size */
+		args->inbuf_p %= INPUT_BUFFER_SIZE;
+		if (args->inbuf_p == 0) {
+			m = MIN(INPUT_BUFFER_SIZE,
+			    (args->len - args->counter_value));
+			ret = read(args->fd, args->inbuf, m);
+			args->inbuf_l = ret;
+		}
+		if (args->inbuf_p < args->inbuf_l) {
+			buf[0] = args->inbuf[args->inbuf_p++];
+			ret = 1;
+		} else {
+			args->inbuf_p = 0;
+			ret = 0;
+		}
+	}
+	if (args->enable_counter) {
+		args->counter_value += ret;
+	}
+
+	return (ret);
+}
+
+static int
+read_line(struct args *args, char *line, int maxlen)
+{
+	int i;
+
+	for (i = 0; i < maxlen; i ++) {
+		if (local_read(args, line + i, 1) != 1) {
+			return (1);
+		}
+
+		if (line[i] == '\r')
+			line[i] = '\0';
+		else if (line[i] == '\n') {
+			line[i] = '\0';
+			break;
+		}
+	}
+
+	if (i == maxlen)
+		line[maxlen - 1] = '\0';
+
+	return (0);
+}
+
+
+static void
+parse_boundary(char *buf, char *boundary)
+{
+	char *boundaryp;
+
+	/* Content-Type: multipart/form-data; boundary=Asrf456BGe4h*/
+	boundaryp = strnstr(buf + strlen("Content-Type:"), "boundary=",
+	    LINE_BUFFER_LEN - strlen("Content-Type:"));
+	if (boundaryp) {
+		/* Check if we have multipart/form-data */
+		if (!strstr(buf, "multipart/form-data"))
+			return;
+		/* skip field name */
+		boundaryp += strlen("boundary=");
+		/* stop on any whitespace */
+		boundaryp = strsep(&boundaryp, " \t\r\n");
+		if (strlen(boundaryp) < 1)
+			return;
+		strcpy(boundary, boundaryp);
+		return;
+	}
+	/* TODO: warning here */
+	return;
+}
+
+static void
+parse_len(char *buf, unsigned int *len)
+{
+	char *lenp, *elenp;
+	int slen;
+
+	/* Content-Length: 6268237 */
+	lenp = buf += strlen("Content-Length:");
+	skip_spaces(&lenp);
+	slen = strlen(lenp);
+	if (slen < 1)
+		return;
+	*len = strtoul(lenp, &elenp, 10);
+	if ((elenp - lenp) < slen) {
+		*len = 0;
+		/* TODO: warning here */
+	}
+	return;
+}
+
+static void
+parse_hash(char *buf, char *hash)
+{
+	char *hashp, *p;
+
+	/* Authorization: Basic YWRtaW46YWRtaW4= */
+	hashp = strnstr(buf + strlen("Authorization:"), "Basic",
+	    LINE_BUFFER_LEN - strlen("Authorization:"));
+	if (hashp) {
+		/* skip field name */
+		hashp += strlen("Basic");
+		/* skip spaces and tabs */
+		skip_spaces(&hashp);
+		/* stop on any whitespace */
+		p = hashp;
+		p = strsep(&p, " \t\r\n");
+		if (strlen(hashp) < 2)
+			return;
+		strcpy(hash, hashp);
+		return;
+	}
+	/* TODO: warning here */
+	return;
+}
+
+static int
+test_part_header(struct args *args, char *boundary, char *buf)
+{
+	char *p, filename[256];
+	int i, match, err;
+
+	args->filename[0] = args->filename[255] = '\0';
+	match = err = 0;
+
+	if (read_line(args, buf, LINE_BUFFER_LEN))
+		return (1);
+
+	if (strncmp(buf, "Content-Disposition:",
+	    strlen("Content-Disposition:")) == 0) {
+		p = buf + strlen("Content-Disposition:");
+		skip_spaces(&p);
+		if (strncmp(p, "form-data", strlen("form-data")) != 0)
+			return (0);
+		p += strlen("form-data");
+
+		for (i = 0; i < 8; i++) {
+			strsep(&p, ";");
+			if (p == 0 || *p == '\0')
+				break;
+			p += strspn(p, " \t");
+			if (strncmp(p, "name=\"firmware\"",
+			    strlen("name=\"firmware\"")) == 0)
+				match ++;
+			if (strncmp(p, "filename=\"",
+			    strlen("filename=\"")) == 0) {
+				p += strlen("filename=\"");
+				bzero(filename, sizeof(filename));
+				strncpy(filename, p, index(p, '\"') - p);
+				if (filename[0] == '\0')
+					return (1);
+				match ++;
+			}
+			if (match == 2)
+				break;
+		}
+	}
+	/* Skip part header lines until empty line */
+	while ((err = read_line(args, buf, LINE_BUFFER_LEN)) == 0) {
+		if (err != 0)
+			return (1);
+		if (strlen(buf) == 0)
+			break;
+	}
+	if (match == 2) {
+		return (2); /* ready for recive firmware */
+	}
+	char c;
+	while (local_read(args, &c, 1) == 1)
+		if (c == '\n' && 
+		    (local_read(args, &c, 1) == 1) && c == '-' &&
+		    (local_read(args, &c, 1) == 1) && c == '-') {
+			if (read_line(args, buf, LINE_BUFFER_LEN))
+				return (1);
+			if (strncmp(buf, boundary, strlen(boundary)) == 0) {
+				p = buf + strlen(boundary);
+				if (p[0] == '-' && p[1] == '-') {
+					/* return if form data over */
+					return (1);
+				} else {
+					break;
+				}
+			}
+		}
+	/* Boundary marker skipped, start new part */
+	return (test_part_header(args, boundary, buf));
+}
+
+enum http_input_status
+http_input(struct args *args, int console)
+{
+	char buf[LINE_BUFFER_LEN], hash[256], boundary[71], ch;
+	int err, post, i, out;
+	struct rbuffer *rb;
+
+	err = 1;
+	boundary[0] = hash[0] = post = 0;
+	args->console = console;
+
+	/* POST /send-message.html HTTP/1.1 */
+	if (read_line(args, buf, LINE_BUFFER_LEN)) {
+		http_error("Can't accept methods other than GET or POST");
+		return (HTTP_INPUT_READ_ERR);
+	}
+
+	if (strncmp(buf, "GET", 3) == 0)
+		post = 0;
+	else if (strncmp(buf, "POST", 4) == 0)
+		post = 1;
+	else {
+		http_error("Can't accept methods other than GET or POST");
+		return (HTTP_INPUT_METHOD_ERR);
+	}
+
+	for (;;) {
+		if (read_line(args, buf, LINE_BUFFER_LEN)) {
+			dprintf(console, "Can't get input line\n");
+			return (HTTP_INPUT_READ_ERR);
+		}
+		if (!boundary[0] && (strncmp(buf, "Content-Type:",
+		    strlen("Content-Type:")) == 0))
+			parse_boundary(buf, boundary);
+		if (!args->len && (strncmp(buf, "Content-Length:",
+		    strlen("Content-Length:")) == 0))
+			parse_len(buf, &args->len);
+		if (!hash[0] && (strncmp(buf, "Authorization:",
+		    strlen("Authorization:")) == 0))
+			parse_hash(buf, hash);
+		if (buf[0] == '\0') /* Empty line */
+			break;
+	}
+
+	/* Header parsing done, run counter */
+	args->enable_counter = 1;
+
+	/* Check authentication if hash supplied */
+	if (args->key && args->realm) {
+		if (strcmp(hash, args->key) != 0) {
+			http_unauth(args);
+			dprintf(console,
+			    "Sent Authorization required message\n");
+			return (HTTP_INPUT_AUTH_FAIL);
+		}
+	}
+
+	/*
+	 * We have two non error answers:
+	 * 1. Page with form for GET requiest.
+	 */
+	if (post == 0) {
+		http_index();
+		dprintf(console, "Sent index page to user\n");
+		return (HTTP_INPUT_SHOW_INDEX);
+	}
+	if (!boundary[0] || !args->len) {
+		dprintf(console, "\n");
+		http_error("<font class=\"red\">" CRLF
+		    "  Multipart form must have both boundary marker and "
+			"Content-Length defined." CRLF
+		    "</font>" CRLF);
+		return (HTTP_INPUT_BAD_MULTIPART);
+	}
+
+	dprintf(console, "boundary=\"%s\", body length=%d\n",
+	    boundary, args->len);
+
+	/*
+	 * 2. Parse/check image and post success notification for POST
+	 * requiest.
+	 */
+	if (read_line(args, buf, LINE_BUFFER_LEN)) {
+		http_error("<font class=\"red\">" CRLF
+		    "Failed to parse form data." CRLF
+		    "</font>" CRLF);
+		return (HTTP_INPUT_READ_ERR);
+	}
+
+	/* Multipart for body must start with boundary marker */
+	if ((buf[0] == '-') &&
+	    (buf[1] == '-') &&
+	    (strncmp(buf+2, boundary, strlen(boundary)) == 0)) {
+		/* Find firmware part. */
+		if (test_part_header(args, boundary, buf) != 2) {
+			http_error("<font class=\"red\">" CRLF
+			    "No Firmware image in posted data." CRLF
+			    "</font>" CRLF);
+			return (HTTP_INPUT_FW_PART_REQUIRED);
+		}
+	}
+
+	out = open(args->outfile, O_WRONLY, 0644);
+	if (out < 0) {
+		dprintf(console, "Can't open \"%s\" file\n", args->outfile);
+		err = HTTP_INPUT_CANT_OPEN_OFILE;
+		goto return_err;
+	}
+
+	rb = ring_buffer_init(boundary, out);
+	if (!rb) {
+		dprintf(console, "Can't init rb\n");
+		err = HTTP_INPUT_RB_INIT_ERR;
+		goto close_out;
+	}
+
+	for (i = args->counter_value; i < args->len; i++) {
+		if (local_read(args, &ch, 1) != 1) {
+			/* Error on read */
+			dprintf(args->console, "Error on read @ count=%d\n",
+			    args->counter_value);
+			break;
+		}
+		if (ring_buffer_append(rb, ch)) {
+			/*
+			 * Found next or last boundary marker, nothing to do
+			 * anymore.
+			 */
+			ring_buffer_drain(rb);
+			args->filesize = ring_buffer_get_filesize(rb);
+
+			/* Drain everything left in socket */
+			fcntl(args->fd, F_SETFL, O_NONBLOCK);
+
+			for (; args->counter_value != args->len; )
+				local_read(args, buf, 1);
+
+			dprintf(console,
+			    "POST parsing done, we get new FW\n");
+			http_ok();
+			err = HTTP_INPUT_READY;
+
+			break;
+		}
+	}
+
+	if (err) {
+		dprintf(console,
+		    "POST parsing failed, last boundary marker not found\n");
+		http_fail();
+	}
+
+	ring_buffer_free(rb);
+close_out:
+	local_close(out);
+return_err:
+
+	return (err);
+}
+
+#ifdef MODULE_TEST
+int 
+main(int argc, char **argv)
+{
+	struct args args;
+	int console;
+
+	bzero(&args, sizeof(args));
+
+	if (argc < 2)
+		exit(1);
+	console = open("/dev/stdout", O_WRONLY);
+
+	args.fd = open(argv[1], O_RDONLY);
+	args.key = "YWRtaW46YWRtaW4=";
+	args.outfile = "./test.out";
+	http_input(&args, console);
+	close(args.fd);
+	close(console);
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target/sbin/upgrade/http_input.h	Fri Sep 14 02:16:17 2012 +0300
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2012 Rybalko Aleksandr.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define	LINE_BUFFER_LEN		256
+#define	INPUT_BUFFER_SIZE	1024
+
+struct args {
+	int		console;
+	int		fd;			/* Input FD */
+	unsigned int	len;			/* Body len from Header */
+	int		counter_value;		/* Current body position */
+	int		enable_counter;		/* Switch header ->> body */
+
+	char		*key;			/* Basic auth hash */
+	char		*realm;;		/* HTTP Basic auth realm */
+
+	char		*outfile;		/* Output filename */
+	char		filename[255];		/* Firmware filename from form
+						 data */
+	unsigned int	filesize;		/* Collected file size */
+	char		boundary[71];		/* Multipart boundary marker */
+	unsigned int	inbuf_p;
+	unsigned int	inbuf_l;
+	char		inbuf[INPUT_BUFFER_SIZE];
+};
+
+enum http_input_status {
+	HTTP_INPUT_READY = 0,
+	HTTP_INPUT_SHOW_INDEX,
+	HTTP_INPUT_AUTH_FAIL,
+	HTTP_INPUT_BAD_MULTIPART,
+	HTTP_INPUT_CANT_OPEN_OFILE,
+	HTTP_INPUT_FW_PART_REQUIRED,
+	HTTP_INPUT_METHOD_ERR,
+	HTTP_INPUT_RB_INIT_ERR,
+	HTTP_INPUT_READ_ERR
+};
+
+enum http_input_status	http_input(struct args *args, int console);
+
--- a/target/sbin/upgrade/image.h	Fri Sep 14 02:13:58 2012 +0300
+++ b/target/sbin/upgrade/image.h	Fri Sep 14 02:16:17 2012 +0300
@@ -1,14 +1,40 @@
+/*-
+ * Copyright (c) 2010 Rybalko Aleksandr.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
 #ifndef _IMAGE_H_
 #define _IMAGE_H_
 
 struct image_header {
 	char     image_name[32];	/* 0x00 */
-	uint32_t magic_kernel;		/* 0x20  */
-	uint32_t magic_rootfs;		/* 0x24  */
-	uint32_t image_size;		/* 0x28  */
-	uint32_t image_offset;		/* 0x2c  */
-	char     image_device[32];	/* 0x30  */
-	uint8_t  image_digest[16];	/* 0x50  */
+	uint32_t magic_kernel;		/* 0x20 */
+	uint32_t magic_rootfs;		/* 0x24 */
+	uint32_t image_size;		/* 0x28 */
+	uint32_t image_offset;		/* 0x2c */
+	char     image_device[32];	/* 0x30 */
+	uint8_t  image_digest[16];	/* 0x50 */
 };
 
 struct image_split_header {
--- a/target/sbin/upgrade/upgrade.c	Fri Sep 14 02:13:58 2012 +0300
+++ b/target/sbin/upgrade/upgrade.c	Fri Sep 14 02:16:17 2012 +0300
@@ -1,13 +1,28 @@
-/********************************************************************
-* Description:
-* Author: Alex RAY <>
-* Created at: Sat Mar 20 01:23:01 EET 2010
-* Computer: terran.dlink.ua 
-* System: FreeBSD 8.0-RELEASE-p2 on i386
-*    
-* Copyright (c) 2010 Alex RAY  All rights reserved.
-*
-********************************************************************/
+/*-
+ * Copyright (c) 2012 Rybalko Aleksandr.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
 
 /*
  * What ./upgrade do
@@ -25,23 +40,38 @@
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <sys/reboot.h>
-#ifdef USE_MD5
-#include <md5.h>
-#endif
+#define _WITH_DPRINTF
+#include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+#ifdef USE_MD5
+#include <md5.h>
+#endif
+
 #include "image.h"
+#ifdef WITH_HTTP_MODE
+#include "http_input.h"
+#endif
 
 #define SYSCTL "kern.geom.debugflags"
 
+#ifndef	DEFAULT_DEVICE
 #define DEFAULT_DEVICE "/dev/map/upgrade"
+#endif
+
+#ifdef USE_GEOM_GET_SIZE
+size_t	geom_get_size(char *);
+#endif
+
+int console;
 
 void usage()
 {
-	printf(
+	dprintf(console, 
 	    "./upgrade [-s 0x10000] [-R] [-V] -f new.img "
 		"[-d /dev/device]\n"
 	    "\t-f img     - New system image filename\n"
@@ -49,13 +79,20 @@
 	    "\t-q         - Be silent\n"
 	    "\t-R         - Don`t reboot when done\n"
 	    "\t-s 0x10000 - use blocksize, default 64k\n"
+#ifdef USE_MD5
 	    "\t-V         - Don`t verify image\n"
+#endif
+#ifdef WITH_HTTP_MODE
+	    "\t-h         - Enable HTTP mode\n"
+	    "\t-H hash==  - HTTP Auth hash\n"
+	    "\t-M realm   - HTTP Auth realm\n"
+#endif
 	    );
 	exit(1);
 }
 
 #ifdef USE_MD5
-static int check_md5(FILE * fh, int blocksize)
+static int check_md5(int fh, int blocksize)
 {
 	struct image_header header;
 	unsigned char digest[16];
@@ -65,16 +102,16 @@
 
 	if (!buf)
 	{
-		printf("Error allocating buffer[%d]\n", blocksize);
+		dprintf(console, "Error allocating buffer[%d]\n", blocksize);
 		return 1;
 	}
 
-	rewind(fh);
+	lseek(fh, 0, SEEK_SET);
 
-	if (fread((char *)&header, 1, sizeof(struct image_header), fh) != 
+	if (read(fh, (char *)&header, sizeof(struct image_header)) != 
 		sizeof(struct image_header))
 	{
-		printf("Error reading file header\n");
+		dprintf(console, "Error reading file header\n");
 		free(buf);
 		return 1;
 	}
@@ -84,7 +121,7 @@
 	MD5Update(&context,  header.image_device, sizeof(header.image_device));
 	size = header.image_size;
 
-	for ( ; (i = fread(buf, 1, (size > blocksize)?blocksize:size, fh));
+	for ( ; (i = read(fh, buf, (size > blocksize)?blocksize:size));
 		size -= i)
 	{
 		MD5Update(&context, buf, i);
@@ -106,7 +143,7 @@
 
 	len = sizeof(val);
 	if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) {
-		printf(SYSCTL " sysctl missing\n");
+		dprintf(console, SYSCTL " sysctl missing\n");
 		exit(1);
 	}
 	if (!(val & 16)) {
@@ -119,19 +156,36 @@
 
 int main(int argc, char **argv)
 {
-	int ch, i, c;
+	int ch, i, c, status, bytes;
+	int check = 1, do_sync = 1, rebt = 1, silent = 0;
+	int blocksize = 0x10000, exitcode = 0;
+	char *file = 0, *device = DEFAULT_DEVICE, *buf;
+	FILE *fh;
+	int ofh;
+#ifdef USE_GEOM_GET_SIZE
+	int target_size_check = 1;
+	size_t target_size;
+#endif
 #ifdef USE_MD5
 	int verify = 1;
 #endif
-	int check = 1, do_sync = 1, rebt = 1, silent = 0;
-	int blocksize = 0x10000, exitcode = 0;
-	char *file = 0, *device = DEFAULT_DEVICE, *buf;
-	FILE *fh, *ofh;
+#ifdef WITH_HTTP_MODE
+	int http_mode = 0;
+	struct args args;
+#endif
 
-	while ((ch = getopt(argc, argv, "f:d:qRSs:"
+	console = STDOUT_FILENO;
+	status = 0;
+	while ((ch = getopt(argc, argv, "f:d:l:qRSs:"
+#ifdef USE_GEOM_GET_SIZE
+	    "G"
+#endif
 #ifdef USE_MD5
 	    "V"
 #endif
+#ifdef WITH_HTTP_MODE
+	    "hH:M:"
+#endif
 	    )) != -1)
 		switch (ch) {
 		case 'f':
@@ -140,6 +194,14 @@
 		case 'd':
 			device = optarg;
 			break;
+#ifdef USE_GEOM_GET_SIZE
+		case 'G':
+			target_size_check = 0;
+			break;
+#endif
+		case 'l':
+			console = open(optarg, O_WRONLY);
+			break;
 		case 'q':
 			silent = 1;
 			break;
@@ -152,27 +214,122 @@
 		case 's':
 			blocksize = strtoul(optarg, 0, 0);
 			break;
+#ifdef WITH_HTTP_MODE
+		case 'h':
+			http_mode = 1;
+			break;
+		case 'H':
+			args.key = optarg;
+			break;
+		case 'M':
+			args.realm = optarg;
+			break;
+#endif
 #ifdef USE_MD5
 		case 'V':
 			verify = 0;
 			break;
 #endif
 		default:
+			dprintf(console, "Wrong key %c\n", ch);
 			usage();
 		}
 	argv += optind;
 
-	if (!file) usage();
+
+	if (!file) {
+		dprintf(console, "Input filename required\n");
+		usage();
+	}
+
+#ifdef WITH_HTTP_MODE
+	if (http_mode == 1) {
+#ifdef USE_MD5
+		verify = 0;	/* XXX: just for now */
+#endif
+		bzero(&args, sizeof(args));
+		/* inetd mode, read request from STDIN */
+		args.fd = STDIN_FILENO;
+		args.outfile = file;
+
+		status = http_input(&args, console);
+
+		/*
+		 * XXX: Should parse Firmware filename from POST data to get
+		 * MD5 sum here. That will allow verify file checksum.
+		 * it is args.filename.
+		 */
+		switch (status) {
+		case HTTP_INPUT_READY:
+			/* We get FW image in file $file */
+			break;
+		case HTTP_INPUT_SHOW_INDEX:
+		case HTTP_INPUT_AUTH_FAIL:
+			exitcode = 0;
+			goto just_exit;
+		case HTTP_INPUT_BAD_MULTIPART:
+			if (!silent)
+				dprintf(console, "Multipart format error\n");
+			exitcode = 0;
+			goto just_exit;
+		case HTTP_INPUT_CANT_OPEN_OFILE:
+			if (!silent)
+				dprintf(console,
+				    "Can't open \"%s\" file for writing\n");
+			exitcode = 0;
+			goto just_exit;
+		case HTTP_INPUT_FW_PART_REQUIRED:
+			if (!silent)
+				dprintf(console, "Lack of FW part\n");
+			exitcode = 1;
+			goto just_exit;
+		case HTTP_INPUT_METHOD_ERR:
+			if (!silent)
+				dprintf(console, "Wrong HTTP method\n");
+			exitcode = 1;
+			goto just_exit;
+		case HTTP_INPUT_RB_INIT_ERR:
+			if (!silent)
+				dprintf(console,
+				    "Can't allocate ringbuffer\n");
+			exitcode = 1;
+			goto just_exit;
+		case HTTP_INPUT_READ_ERR:
+			if (!silent)
+				dprintf(console, "Error on read input\n");
+			exitcode = 1;
+			goto just_exit;
+		}
+	}
+#endif
+
+#ifdef USE_GEOM_GET_SIZE
+	if (target_size_check)
+		target_size = geom_get_size(device);
+		if (target_size == 0) {
+			dprintf(console, "Can't get size of %s\n", device);
+			exitcode = 1;
+			goto just_exit;
+		}
+#ifdef WITH_HTTP_MODE
+		if (args.filesize > target_size) {
+			dprintf(console, "Input file too big to write into "
+			    "%s\n", device);
+			exitcode = 1;
+			goto just_exit;
+		}
+#endif
+#endif
 	check_geom_debugflags16_enabled();
 
-	if (!silent) printf("Use file %s\n", file);
-	if (!silent) printf("Will write to %s\n", device);
-	if (!silent) printf("Use blocksize %#x\n", blocksize);
+	if (!silent) dprintf(console, "Use file %s\n", file);
+	if (!silent) dprintf(console, "Will write to %s\n", device);
+	if (!silent) dprintf(console, "Use blocksize %#x\n", blocksize);
 
 	fh = fopen(file, "r");
 	if ( !fh )
 	{
-		printf("Error opening file %s\n", file);
+		dprintf(console, "Error opening file %s\n", file);
 		exitcode = 1;
 		goto just_exit;
 	}
@@ -180,7 +337,7 @@
 	buf = malloc(blocksize);
 	if (!buf)
 	{
-		printf("Error allocating blocksize=%#x\n", blocksize);
+		dprintf(console, "Error allocating blocksize=%#x\n", blocksize);
 		exitcode = 2;
 		goto free_exit;
 	}
@@ -189,12 +346,13 @@
 	if (verify) check = check_md5(fh, blocksize);
 #endif
 	if (!silent && !check )
-		printf("Image check - %s\n", check?"FAIL":"ok" );
+		dprintf(console, "Image check - %s\n", check?"FAIL":"ok" );
 
-	ofh = fopen(device, "r+");
-	if ( !ofh )
+	ofh = open(device, O_RDWR|O_DIRECT|O_SYNC);
+	if ( ofh < 0 )
 	{
-		printf("Error opening device %s\n", device);
+		dprintf(console, "Error opening device %s (errno=%d)\n",
+		    device, errno);
 		exitcode = 1;
 		goto close2_exit;
 	}
@@ -204,30 +362,45 @@
 	bzero(buf, blocksize);
 	/* Non buffered io for stdout */
 	setvbuf(stdout, NULL, _IONBF, 0);
-	/* Non buffered io for flash device, to avoid use of big memory chunks*/
-	setvbuf(ofh, NULL, _IONBF, 0);
 
-	for ( c = 0; (i = fread(buf, 1, blocksize, fh)); c++ )
+	/*
+	 * -- Non buffered io for flash device, to avoid use of big memory
+	 * chunks. --
+	 */
+	/*
+	 * REMOVED for output, otherwise fwrite will split blocks by 1024B
+	 * pieces.
+	 */
+
+	for ( c = 0, bytes = 0; (i = fread(buf, 1, blocksize, fh));
+	    c++, bytes += blocksize )
 	{
 		if (!silent) {
-			if (c % 10) printf(".");
-			else      printf("%d", c);
+			if (c % 10) dprintf(console, ".");
+			else      dprintf(console, "%d", c);
 		}
 		/* always write blocksize, not "i", like `dd conv=sync` */
-		if (fwrite(buf, blocksize, 1, ofh) != 1)
+		if (write(ofh, buf, blocksize) != blocksize)
 		{
-		    printf("Error when writing to %s, "
-			"continue trying to make it done\n", device);
+		    dprintf(console, "Error when writing to %s, "
+			"continue, trying to make it done (errno=%d)\n",
+			device, errno);
 		    exitcode = 7;
 		}
+#ifdef WITH_HTTP_MODE
+		if ((http_mode == 1) && (bytes >= args.filesize)) {
+			/* Does not copy empty blocks */
+			break;
+		}
+#endif
 		bzero(buf, blocksize);
 	}
-	printf("\n");
+	dprintf(console, "\n");
 
 	/* sync */
 	if (do_sync) {
 		if (!silent)
-			printf("Sync buffers\n");
+			dprintf(console, "Sync buffers\n");
 		sync();
 		sync();
 		sync();
@@ -236,23 +409,19 @@
 #ifdef USE_MD5
 	if (verify) {
 		if (!silent)
-			printf("Verify md5 sum\n");
+			dprintf(console, "Verify md5 sum\n");
 		check = check_md5(ofh, blocksize);
 		if (check)
 		{
-			printf("Verification fail\n");
+			dprintf(console, "Verification fail\n");
 			exitcode = 7;
 		}
 	}
 #endif
 
-	fclose(ofh);
+	close(ofh);
 	ofh = 0;
 
-	if (!silent)
-		printf("Sleep 5 seconds...\n");
-	sleep(5); /* For make shure, what flash write done */
-
 	if (rebt && !exitcode)
 	{
 		/*
@@ -260,28 +429,28 @@
 		 * if write return error, let user to fix it
 		 * maybe filesystem still alive
 		 */
-		printf("Write done, now rebooting\n");
+		dprintf(console, "Write done, now rebooting\n");
 		if (do_sync) {
 			if (!silent)
-				printf("reboot now ...\n");
+				dprintf(console, "reboot now ...\n");
 			reboot(RB_AUTOBOOT);
 		} else {
 			if (!silent)
-				printf("reboot w/o sync now ...\n");
+				dprintf(console, "reboot w/o sync now ...\n");
 			reboot(RB_AUTOBOOT|RB_NOSYNC);
 		}
 	}
 
 close2_exit:
 	if (ofh)
-		fclose(ofh);
+		close(ofh);
 free_exit:
 	free(buf);
 	fclose(fh);
 just_exit:
+	if (console != STDOUT_FILENO)
+		close(console);
 	exit(exitcode);
 
 }
 
-
-