Releasing debian version 4.06+dfsg-2.
[syslinux-debian/hramrach.git] / com32 / libupload / upload_tftp.c
blob5e73c1c55bcd3f2155d912345121347c00958302
1 /*
2 * TFTP data output backend
3 */
5 #include <string.h>
6 #include <stdio.h>
7 #include <syslinux/pxe.h>
8 #include <syslinux/config.h>
9 #include <netinet/in.h>
10 #include <sys/times.h>
11 #include "upload_backend.h"
13 enum tftp_opcode {
14 TFTP_RRQ = 1,
15 TFTP_WRQ = 2,
16 TFTP_DATA = 3,
17 TFTP_ACK = 4,
18 TFTP_ERROR = 5,
21 struct tftp_error {
22 uint16_t opcode;
23 uint16_t errcode;
24 char errmsg[0];
25 } __attribute__ (( packed ));
27 struct tftp_state {
28 uint32_t my_ip;
29 uint32_t srv_ip;
30 uint32_t srv_gw;
31 uint16_t my_port;
32 uint16_t srv_port;
33 uint16_t seq;
36 const char *tftp_string_error_message[]={
37 "",
38 "File not found",
39 "Access Denied",
40 "Disk Full",
41 "Illegal Operation",
42 "Unknown Transfert ID",
43 "File already exists",
44 "Unknown User",
45 "Negociation failed",
46 "Unable to resolve hostname", // not in RFC
47 "Unable to connect", // not in RFC
48 "No Error",
51 #define RCV_BUF 2048
53 static int send_ack_packet(struct tftp_state *tftp,
54 const void *pkt, size_t len)
56 com32sys_t ireg, oreg;
57 t_PXENV_UDP_WRITE *uw;
58 t_PXENV_UDP_READ *ur;
59 clock_t start;
60 static const clock_t timeouts[] = {
61 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31,
62 37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0
64 const clock_t *timeout;
65 int err = -1;
67 uw = lmalloc(sizeof *uw + len);
68 ur = lmalloc(sizeof *ur + RCV_BUF);
70 memset(&ireg, 0, sizeof ireg);
71 ireg.eax.w[0] = 0x0009;
73 for (timeout = timeouts ; *timeout ; timeout++) {
74 memset(uw, 0, sizeof *uw);
75 memcpy(uw+1, pkt, len);
76 uw->ip = tftp->srv_ip;
77 uw->gw = tftp->srv_gw;
78 uw->src_port = tftp->my_port;
79 uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69);
80 uw->buffer_size = len;
81 uw->buffer = FAR_PTR(uw+1);
83 ireg.ebx.w[0] = PXENV_UDP_WRITE;
84 ireg.es = SEG(uw);
85 ireg.edi.w[0] = OFFS(uw);
87 __intcall(0x22, &ireg, &oreg);
89 start = times(NULL);
91 do {
92 memset(ur, 0, sizeof *ur);
93 ur->src_ip = tftp->srv_ip;
94 ur->dest_ip = tftp->my_ip;
95 ur->s_port = tftp->srv_port;
96 ur->d_port = tftp->my_port;
97 ur->buffer_size = RCV_BUF;
98 ur->buffer = FAR_PTR(ur+1);
100 ireg.ebx.w[0] = PXENV_UDP_READ;
101 ireg.es = SEG(ur);
102 ireg.edi.w[0] = OFFS(ur);
103 __intcall(0x22, &ireg, &oreg);
105 if (!(oreg.eflags.l & EFLAGS_CF) &&
106 ur->status == PXENV_STATUS_SUCCESS &&
107 tftp->srv_ip == ur->src_ip &&
108 (tftp->srv_port == 0 ||
109 tftp->srv_port == ur->s_port)) {
110 uint16_t *xb = (uint16_t *)(ur+1);
111 if (ntohs(xb[0]) == TFTP_ACK &&
112 ntohs(xb[1]) == tftp->seq) {
113 tftp->srv_port = ur->s_port;
114 err = TFTP_OK; /* All good! */
115 goto done;
116 } else if (ntohs(xb[0]) == TFTP_ERROR) {
117 struct tftp_error *te = (struct tftp_error *)(ur+1);
118 if (te->errcode == TFTP_ERR_UNKNOWN_ERROR) {
119 tftp_string_error_message[TFTP_ERR_UNKNOWN_ERROR]=strdup(te->errmsg);
121 err=-ntohs(te->errcode); // Return the associated error code
122 goto done;
125 } while ((clock_t)(times(NULL) - start) < *timeout);
128 done:
129 lfree(ur);
130 lfree(uw);
132 return err;
135 static int upload_tftp_write(struct upload_backend *be)
137 static uint16_t local_port = 0x4000;
138 struct tftp_state tftp;
139 char buffer[512+4+6];
140 int nlen;
141 int err=TFTP_OK;
142 const union syslinux_derivative_info *sdi =
143 syslinux_derivative_info();
144 const char *data = be->outbuf;
145 size_t len = be->zbytes;
146 size_t chunk;
148 tftp.my_ip = sdi->pxe.myip;
149 tftp.my_port = htons(local_port++);
150 tftp.srv_gw = ((tftp.srv_ip ^ tftp.my_ip) & sdi->pxe.ipinfo->netmask)
151 ? sdi->pxe.ipinfo->gateway : 0;
152 tftp.srv_port = 0;
153 tftp.seq = 0;
155 if (be->argv[1]) {
156 tftp.srv_ip = pxe_dns(be->argv[1]);
157 if (!tftp.srv_ip) {
158 // printf("\nUnable to resolve hostname: %s\n", be->argv[1]);
159 return -TFTP_ERR_UNABLE_TO_RESOLVE;
161 } else {
162 tftp.srv_ip = sdi->pxe.ipinfo->serverip;
163 if (!tftp.srv_ip) {
164 // printf("\nNo server IP address\n");
165 return -TFTP_ERR_UNABLE_TO_CONNECT;
169 /* printf("server %u.%u.%u.%u... ",
170 ((uint8_t *)&tftp.srv_ip)[0],
171 ((uint8_t *)&tftp.srv_ip)[1],
172 ((uint8_t *)&tftp.srv_ip)[2],
173 ((uint8_t *)&tftp.srv_ip)[3]);*/
175 buffer[0] = 0;
176 buffer[1] = TFTP_WRQ;
177 nlen = strlcpy(buffer+2, be->argv[0], 512);
178 memcpy(buffer+3+nlen, "octet", 6);
180 if ((err=send_ack_packet(&tftp, buffer, 2+nlen+1+6))!=TFTP_OK)
181 return err;
183 do {
184 chunk = len >= 512 ? 512 : len;
186 buffer[1] = TFTP_DATA;
187 *((uint16_t *)(buffer+2)) = htons(++tftp.seq);
188 memcpy(buffer+4, data, chunk);
189 data += chunk;
190 len -= chunk;
192 if ((err=send_ack_packet(&tftp, buffer, chunk+4))!=TFTP_OK)
193 return err;
194 } while (chunk == 512);
196 return TFTP_OK;
199 struct upload_backend upload_tftp = {
200 .name = "tftp",
201 .helpmsg = "filename [tftp_server]",
202 .minargs = 1,
203 .write = upload_tftp_write,