aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--com32/libupload/upload_tftp.c179
-rw-r--r--core/fs/pxe/tftp.c170
-rw-r--r--core/fs/pxe/tftp.h11
-rw-r--r--core/fs/pxe/urlparse.c2
-rw-r--r--mk/com32.mk8
5 files changed, 220 insertions, 150 deletions
diff --git a/com32/libupload/upload_tftp.c b/com32/libupload/upload_tftp.c
index 6a0dacb7..387113ba 100644
--- a/com32/libupload/upload_tftp.c
+++ b/com32/libupload/upload_tftp.c
@@ -8,33 +8,12 @@
#include <syslinux/config.h>
#include <netinet/in.h>
#include <sys/times.h>
+#include <fs/pxe/pxe.h>
+#include <fs/pxe/url.h>
#include "upload_backend.h"
-enum tftp_opcode {
- TFTP_RRQ = 1,
- TFTP_WRQ = 2,
- TFTP_DATA = 3,
- TFTP_ACK = 4,
- TFTP_ERROR = 5,
-};
-
-struct tftp_error {
- uint16_t opcode;
- uint16_t errcode;
- char errmsg[0];
-} __attribute__ (( packed ));
-
-struct tftp_state {
- uint32_t my_ip;
- uint32_t srv_ip;
- uint32_t srv_gw;
- uint16_t my_port;
- uint16_t srv_port;
- uint16_t seq;
-};
-
const char *tftp_string_error_message[]={
-"",
+"Unknown error",
"File not found",
"Access Denied",
"Disk Full",
@@ -48,140 +27,46 @@ const char *tftp_string_error_message[]={
"No Error",
};
-#define RCV_BUF 2048
-
-static int send_ack_packet(struct tftp_state *tftp,
- const void *pkt, size_t len)
-{
- t_PXENV_UDP_WRITE *uw;
- t_PXENV_UDP_READ *ur;
- clock_t start;
- static const clock_t timeouts[] = {
- 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
- 37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
- };
- const clock_t *timeout;
- int err = -1;
-
- uw = lmalloc(sizeof *uw + len);
- ur = lmalloc(sizeof *ur + RCV_BUF);
-
- for (timeout = timeouts ; *timeout ; timeout++) {
- memset(uw, 0, sizeof *uw);
- memcpy(uw+1, pkt, len);
- uw->ip = tftp->srv_ip;
- uw->gw = tftp->srv_gw;
- uw->src_port = tftp->my_port;
- uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69);
- uw->buffer_size = len;
- uw->buffer = FAR_PTR(uw+1);
-
- pxe_call(PXENV_UDP_WRITE, uw);
-
- start = times(NULL);
-
- do {
- memset(ur, 0, sizeof *ur);
- ur->src_ip = tftp->srv_ip;
- ur->dest_ip = tftp->my_ip;
- ur->s_port = tftp->srv_port;
- ur->d_port = tftp->my_port;
- ur->buffer_size = RCV_BUF;
- ur->buffer = FAR_PTR(ur+1);
-
- err = pxe_call(PXENV_UDP_READ, ur);
-
- if (!err && ur->status == PXENV_STATUS_SUCCESS &&
- tftp->srv_ip == ur->src_ip &&
- (tftp->srv_port == 0 ||
- tftp->srv_port == ur->s_port)) {
- uint16_t *xb = (uint16_t *)(ur+1);
- if (ntohs(xb[0]) == TFTP_ACK &&
- ntohs(xb[1]) == tftp->seq) {
- tftp->srv_port = ur->s_port;
- err = TFTP_OK; /* All good! */
- goto done;
- } else if (ntohs(xb[0]) == TFTP_ERROR) {
- struct tftp_error *te = (struct tftp_error *)(ur+1);
- if (te->errcode == TFTP_ERR_UNKNOWN_ERROR) {
- tftp_string_error_message[TFTP_ERR_UNKNOWN_ERROR]=strdup(te->errmsg);
- }
- err=-ntohs(te->errcode); // Return the associated error code
- goto done;
- }
- }
- } while ((clock_t)(times(NULL) - start) < *timeout);
- }
-
-done:
- lfree(ur);
- lfree(uw);
-
- return err;
-}
-
-static int upload_tftp_write(struct upload_backend *be)
-{
- static uint16_t local_port = 0x4000;
- struct tftp_state tftp;
- char buffer[512+4+6];
- int nlen;
- int err=TFTP_OK;
+static int upload_tftp_write(struct upload_backend *be) {
const union syslinux_derivative_info *sdi =
syslinux_derivative_info();
- const char *data = be->outbuf;
- size_t len = be->zbytes;
- size_t chunk;
-
- tftp.my_ip = sdi->pxe.myip;
- tftp.my_port = htons(local_port++);
- tftp.srv_gw = ((tftp.srv_ip ^ tftp.my_ip) & sdi->pxe.ipinfo->netmask)
- ? sdi->pxe.ipinfo->gateway : 0;
- tftp.srv_port = 0;
- tftp.seq = 0;
+ struct url_info url;
+ struct inode inode;
+ char url_path[255] = {0};
+ uint32_t ip;
+ int err;
if (be->argv[1]) {
- tftp.srv_ip = pxe_dns(be->argv[1]);
- if (!tftp.srv_ip) {
-// printf("\nUnable to resolve hostname: %s\n", be->argv[1]);
- return -TFTP_ERR_UNABLE_TO_RESOLVE;
- }
+ ip = pxe_dns(be->argv[1]);
+ if (!ip) {
+ dprintf("\nUnable to resolve hostname: %s\n", be->argv[1]);
+ return -TFTP_ERR_UNABLE_TO_RESOLVE;
+ }
} else {
- tftp.srv_ip = sdi->pxe.ipinfo->serverip;
- if (!tftp.srv_ip) {
-// printf("\nNo server IP address\n");
- return -TFTP_ERR_UNABLE_TO_CONNECT;
- }
+ ip = sdi->pxe.ipinfo->serverip;
+ if (!ip) {
+ dprintf("\nNo server IP address\n");
+ return -TFTP_ERR_UNABLE_TO_CONNECT;
+ }
}
-/* printf("server %u.%u.%u.%u... ",
- ((uint8_t *)&tftp.srv_ip)[0],
- ((uint8_t *)&tftp.srv_ip)[1],
- ((uint8_t *)&tftp.srv_ip)[2],
- ((uint8_t *)&tftp.srv_ip)[3]);*/
-
- buffer[0] = 0;
- buffer[1] = TFTP_WRQ;
- nlen = strlcpy(buffer+2, be->argv[0], 512);
- memcpy(buffer+3+nlen, "octet", 6);
-
- if ((err=send_ack_packet(&tftp, buffer, 2+nlen+1+6))!=TFTP_OK)
- return err;
+ snprintf(url_path, sizeof(url_path), "tftp://%u.%u.%u.%u/%s",
+ ((uint8_t *)&ip)[0],
+ ((uint8_t *)&ip)[1],
+ ((uint8_t *)&ip)[2],
+ ((uint8_t *)&ip)[3],
+ be->argv[0]);
- do {
- chunk = len >= 512 ? 512 : len;
+ parse_url(&url, url_path);
+ url.ip = ip;
- buffer[1] = TFTP_DATA;
- *((uint16_t *)(buffer+2)) = htons(++tftp.seq);
- memcpy(buffer+4, data, chunk);
- data += chunk;
- len -= chunk;
+ dprintf("Connecting to %s to send %s\n", url.host, url.path);
+ err = tftp_put(&url, 0, &inode, NULL, be->outbuf, be->zbytes);
- if ((err=send_ack_packet(&tftp, buffer, chunk+4))!=TFTP_OK)
- return err;
- } while (chunk == 512);
+ if (-err != TFTP_OK)
+ printf("upload_tftp_write: TFTP server returned error %d : %s\n", err, tftp_string_error_message[-err]);
- return TFTP_OK;
+ return -err;
}
struct upload_backend upload_tftp = {
diff --git a/core/fs/pxe/tftp.c b/core/fs/pxe/tftp.c
index 113ede2e..594152b9 100644
--- a/core/fs/pxe/tftp.c
+++ b/core/fs/pxe/tftp.c
@@ -423,3 +423,173 @@ done:
return;
}
+
+
+/**
+ * Send a file to a TFTP server
+ *
+ * @param:inode, the inode to store our state in
+ * @param:ip, the ip to contact to get the file
+ * @param:filename, the file we wanna push
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @out: the lenght of this file, stores in file->file_len
+ *
+ */
+__export int tftp_put(struct url_info *url, int flags, struct inode *inode,
+ const char **redir, char *data, int data_length)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ char *buf;
+ uint16_t buf_len;
+ static const char wrq_tail[] = "octet";
+ char wrq_packet_buf[512+4+6];
+ char reply_packet_buf[PKTBUF_SIZE];
+ int err;
+ int wrq_len;
+ const uint8_t *timeout_ptr;
+ jiffies_t timeout;
+ jiffies_t oldtime;
+ uint16_t opcode;
+ uint16_t src_port = url->port;
+ uint32_t src_ip;
+ uint16_t seq = 0;
+ size_t chunk = 0;
+ int len = data_length;
+ int return_code = -ntohs(TFTP_EUNDEF);
+
+ (void)redir; /* TFTP does not redirect */
+ (void)flags;
+
+ if (url->type != URL_OLD_TFTP) {
+ /*
+ * The TFTP URL specification allows the TFTP to end with a
+ * ;mode= which we just ignore.
+ */
+ url_unescape(url->path, ';');
+ }
+
+ if (!src_port)
+ src_port = TFTP_PORT;
+
+// socket->ops = &tftp_conn_ops;
+ if (core_udp_open(socket))
+ return return_code;
+
+ buf = wrq_packet_buf;
+ *(uint16_t *)buf = TFTP_WRQ; /* TFTP opcode */
+ buf += 2;
+
+ buf += strlcpy(buf, url->path, 512);
+
+ buf++; /* Point *past* the final NULL */
+ memcpy(buf, wrq_tail, sizeof wrq_tail);
+ buf += sizeof wrq_tail;
+
+ wrq_len = buf - wrq_packet_buf;
+
+ timeout_ptr = TimeoutTable; /* Reset timeout */
+sendreq:
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ return return_code; /* No file available... */
+ oldtime = jiffies();
+
+ core_udp_sendto(socket, wrq_packet_buf, wrq_len, url->ip, src_port);
+
+ /* If the WRITE call fails, we let the timeout take care of it... */
+ for (;;) {
+ buf_len = sizeof(reply_packet_buf);
+
+ err = core_udp_recv(socket, reply_packet_buf, &buf_len,
+ &src_ip, &src_port);
+ if (err) {
+ jiffies_t now = jiffies();
+ if (now - oldtime >= timeout)
+ goto sendreq;
+ } else {
+ /* Make sure the packet actually came from the server and
+ is long enough for a TFTP opcode */
+ dprintf("tftp_put: got packet buflen=%d from server %u.%u.%u.%u(%u.%u.%u.%u)\n",
+ buf_len,
+ ((uint8_t *)&src_ip)[0],
+ ((uint8_t *)&src_ip)[1],
+ ((uint8_t *)&src_ip)[2],
+ ((uint8_t *)&src_ip)[3],
+ ((uint8_t *)&url->ip)[0],
+ ((uint8_t *)&url->ip)[1],
+ ((uint8_t *)&url->ip)[2],
+ ((uint8_t *)&url->ip)[3]);
+ if ((src_ip == url->ip) && (buf_len >= 2))
+ break;
+ }
+ }
+
+ core_udp_disconnect(socket);
+ core_udp_connect(socket, src_ip, src_port);
+
+ /* filesize <- -1 == unknown */
+ inode->size = -1;
+ socket->tftp_blksize = TFTP_BLOCKSIZE;
+
+ /*
+ * Get the opcode type, and parse it
+ */
+ opcode = *(uint16_t *)reply_packet_buf;
+ switch (opcode) {
+ case TFTP_ERROR:
+ dprintf("tftp_push: received a TFTP_ERROR\n");
+ struct tftp_error *te = (struct tftp_error *)(reply_packet_buf+1);
+ return_code = -ntohs(te->errcode);
+ inode->size = 0;
+ goto done; /* ERROR reply; don't try again */
+
+ case TFTP_ACK:
+ dprintf("tftp_push: received a TFTP_ACK\n");
+ /* We received a ACK packet, sending the associated data packet */
+
+ /* If data was completly sent, we can stop here */
+ if (len == 0) {
+ return_code = -ntohs(TFTP_OK);
+ goto done;
+ }
+
+ /* If the server sequence is not aligned with our, we have an issue
+ * Let's break the transmission for now but could be improved later */
+ uint16_t srv_seq = ntohs(*(uint16_t *)(reply_packet_buf+2));
+ if (srv_seq != seq) {
+ printf("tftp_push: server sequence (%"PRIu16") is not aligned with our sequence (%"PRIu16"\n", srv_seq, seq);
+ return_code = -ntohs(TFTP_EBADOP);
+ goto done;
+ }
+
+ /* Let's transmit the data block */
+ chunk = len >= 512 ? 512 : len;
+ buf = wrq_packet_buf;
+ *(uint16_t *)buf = TFTP_DATA; /* TFTP opcode */
+ *((uint16_t *)(buf+2)) = htons(++seq);
+ memcpy(buf+4, data, chunk);
+ wrq_len = chunk + 4;
+ data += chunk;
+ len -= chunk;
+ timeout_ptr = TimeoutTable; /* Reset timeout */
+ goto sendreq;
+
+ default:
+ dprintf("tftp_push: unknown opcode %d\n", ntohs(opcode));
+ return_code = -ntohs(TFTP_EOPTNEG);
+ goto err_reply;
+ }
+
+err_reply:
+ /* Build the TFTP error packet */
+ dprintf("tftp_push: Failure\n");
+ tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
+ inode->size = 0;
+
+done:
+ if (!inode->size)
+ core_udp_close(socket);
+
+ return return_code;
+}
diff --git a/core/fs/pxe/tftp.h b/core/fs/pxe/tftp.h
index 114c221f..8802914e 100644
--- a/core/fs/pxe/tftp.h
+++ b/core/fs/pxe/tftp.h
@@ -50,5 +50,16 @@
#define TFTP_EEXISTS htons(6) // File exists
#define TFTP_ENOUSER htons(7) // No such user
#define TFTP_EOPTNEG htons(8) // Option negotiation failure
+#define TFTP_ERESOLVE htons(9) // Not in RFC, internal usage
+#define TFTP_ECONNECT htons(10) // Not in RFC, internal usage
+#define TFTP_OK htons(11) // Not in RFC, internal usage
+struct tftp_error {
+ uint16_t opcode;
+ uint16_t errcode;
+ char errmsg[0];
+} __attribute__ (( packed ));
+
+int tftp_put(struct url_info *url, int flags, struct inode *inode,
+ const char **redir, char *data, int data_length);
#endif /* PXE_TFTP_H */
diff --git a/core/fs/pxe/urlparse.c b/core/fs/pxe/urlparse.c
index 6b73ddb6..8b604dca 100644
--- a/core/fs/pxe/urlparse.c
+++ b/core/fs/pxe/urlparse.c
@@ -59,7 +59,7 @@ enum url_type url_type(const char *url)
* this routine does not allocate any additional storage. Freeing the
* original buffer frees all storage used.
*/
-void parse_url(struct url_info *ui, char *url)
+__export void parse_url(struct url_info *ui, char *url)
{
char *p = url;
char *q, *r, *s;
diff --git a/mk/com32.mk b/mk/com32.mk
index 9a3b19d3..90eb7edd 100644
--- a/mk/com32.mk
+++ b/mk/com32.mk
@@ -67,12 +67,16 @@ CFLAGS = $(GCCOPT) $(GCCWARN) \
-fomit-frame-pointer -D__COM32__ -D__FIRMWARE_$(FIRMWARE)__ \
-nostdinc -iwithprefix include \
-I$(com32)/libutil/include -I$(com32)/include \
- -I$(com32)/include/sys $(GPLINCLUDE)
+ -I$(com32)/include/sys $(GPLINCLUDE) \
+ -I$(topdir)/core \
+ -I$(topdir)/core/include
SFLAGS = $(GCCOPT) $(GCCWARN) \
-fomit-frame-pointer -D__COM32__ -D__FIRMWARE_$(FIRMWARE)__ \
-nostdinc -iwithprefix include \
-I$(com32)/libutil/include -I$(com32)/include \
- -I$(com32)/include/sys $(GPLINCLUDE)
+ -I$(com32)/include/sys $(GPLINCLUDE) \
+ -I$(topdir)/core \
+ -I$(topdir)/core/include
COM32LD = $(com32)/lib/$(ARCH)/elf.ld
LDFLAGS = -m elf_$(ARCH) -shared --hash-style=gnu -T $(COM32LD)