summaryrefslogtreecommitdiff
path: root/com32/libupload/upload_tftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'com32/libupload/upload_tftp.c')
-rw-r--r--com32/libupload/upload_tftp.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/com32/libupload/upload_tftp.c b/com32/libupload/upload_tftp.c
new file mode 100644
index 00000000..5e73c1c5
--- /dev/null
+++ b/com32/libupload/upload_tftp.c
@@ -0,0 +1,204 @@
+/*
+ * TFTP data output backend
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <syslinux/pxe.h>
+#include <syslinux/config.h>
+#include <netinet/in.h>
+#include <sys/times.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[]={
+"",
+"File not found",
+"Access Denied",
+"Disk Full",
+"Illegal Operation",
+"Unknown Transfert ID",
+"File already exists",
+"Unknown User",
+"Negociation failed",
+"Unable to resolve hostname", // not in RFC
+"Unable to connect", // not in RFC
+"No Error",
+};
+
+#define RCV_BUF 2048
+
+static int send_ack_packet(struct tftp_state *tftp,
+ const void *pkt, size_t len)
+{
+ com32sys_t ireg, oreg;
+ 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);
+
+ memset(&ireg, 0, sizeof ireg);
+ ireg.eax.w[0] = 0x0009;
+
+ 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);
+
+ ireg.ebx.w[0] = PXENV_UDP_WRITE;
+ ireg.es = SEG(uw);
+ ireg.edi.w[0] = OFFS(uw);
+
+ __intcall(0x22, &ireg, &oreg);
+
+ 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);
+
+ ireg.ebx.w[0] = PXENV_UDP_READ;
+ ireg.es = SEG(ur);
+ ireg.edi.w[0] = OFFS(ur);
+ __intcall(0x22, &ireg, &oreg);
+
+ if (!(oreg.eflags.l & EFLAGS_CF) &&
+ 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;
+ 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;
+
+ 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;
+ }
+ } else {
+ tftp.srv_ip = sdi->pxe.ipinfo->serverip;
+ if (!tftp.srv_ip) {
+// printf("\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;
+
+ do {
+ chunk = len >= 512 ? 512 : len;
+
+ buffer[1] = TFTP_DATA;
+ *((uint16_t *)(buffer+2)) = htons(++tftp.seq);
+ memcpy(buffer+4, data, chunk);
+ data += chunk;
+ len -= chunk;
+
+ if ((err=send_ack_packet(&tftp, buffer, chunk+4))!=TFTP_OK)
+ return err;
+ } while (chunk == 512);
+
+ return TFTP_OK;
+}
+
+struct upload_backend upload_tftp = {
+ .name = "tftp",
+ .helpmsg = "filename [tftp_server]",
+ .minargs = 1,
+ .write = upload_tftp_write,
+};