1 | /* |
---|
2 | * TFTP data output backend |
---|
3 | */ |
---|
4 | |
---|
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" |
---|
12 | |
---|
13 | enum tftp_opcode { |
---|
14 | TFTP_RRQ = 1, |
---|
15 | TFTP_WRQ = 2, |
---|
16 | TFTP_DATA = 3, |
---|
17 | TFTP_ACK = 4, |
---|
18 | TFTP_ERROR = 5, |
---|
19 | }; |
---|
20 | |
---|
21 | struct tftp_error { |
---|
22 | uint16_t opcode; |
---|
23 | uint16_t errcode; |
---|
24 | char errmsg[0]; |
---|
25 | } __attribute__ (( packed )); |
---|
26 | |
---|
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; |
---|
34 | }; |
---|
35 | |
---|
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", |
---|
49 | }; |
---|
50 | |
---|
51 | #define RCV_BUF 2048 |
---|
52 | |
---|
53 | static int send_ack_packet(struct tftp_state *tftp, |
---|
54 | const void *pkt, size_t len) |
---|
55 | { |
---|
56 | t_PXENV_UDP_WRITE *uw; |
---|
57 | t_PXENV_UDP_READ *ur; |
---|
58 | clock_t start; |
---|
59 | static const clock_t timeouts[] = { |
---|
60 | 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, |
---|
61 | 37, 44, 53, 64, 77, 92, 110, 132, 159, 191, 229, 0 |
---|
62 | }; |
---|
63 | const clock_t *timeout; |
---|
64 | int err = -1; |
---|
65 | |
---|
66 | uw = lmalloc(sizeof *uw + len); |
---|
67 | ur = lmalloc(sizeof *ur + RCV_BUF); |
---|
68 | |
---|
69 | for (timeout = timeouts ; *timeout ; timeout++) { |
---|
70 | memset(uw, 0, sizeof *uw); |
---|
71 | memcpy(uw+1, pkt, len); |
---|
72 | uw->ip = tftp->srv_ip; |
---|
73 | uw->gw = tftp->srv_gw; |
---|
74 | uw->src_port = tftp->my_port; |
---|
75 | uw->dst_port = tftp->srv_port ? tftp->srv_port : htons(69); |
---|
76 | uw->buffer_size = len; |
---|
77 | uw->buffer = FAR_PTR(uw+1); |
---|
78 | |
---|
79 | pxe_call(PXENV_UDP_WRITE, uw); |
---|
80 | |
---|
81 | start = times(NULL); |
---|
82 | |
---|
83 | do { |
---|
84 | memset(ur, 0, sizeof *ur); |
---|
85 | ur->src_ip = tftp->srv_ip; |
---|
86 | ur->dest_ip = tftp->my_ip; |
---|
87 | ur->s_port = tftp->srv_port; |
---|
88 | ur->d_port = tftp->my_port; |
---|
89 | ur->buffer_size = RCV_BUF; |
---|
90 | ur->buffer = FAR_PTR(ur+1); |
---|
91 | |
---|
92 | err = pxe_call(PXENV_UDP_READ, ur); |
---|
93 | |
---|
94 | if (!err && ur->status == PXENV_STATUS_SUCCESS && |
---|
95 | tftp->srv_ip == ur->src_ip && |
---|
96 | (tftp->srv_port == 0 || |
---|
97 | tftp->srv_port == ur->s_port)) { |
---|
98 | uint16_t *xb = (uint16_t *)(ur+1); |
---|
99 | if (ntohs(xb[0]) == TFTP_ACK && |
---|
100 | ntohs(xb[1]) == tftp->seq) { |
---|
101 | tftp->srv_port = ur->s_port; |
---|
102 | err = TFTP_OK; /* All good! */ |
---|
103 | goto done; |
---|
104 | } else if (ntohs(xb[0]) == TFTP_ERROR) { |
---|
105 | struct tftp_error *te = (struct tftp_error *)(ur+1); |
---|
106 | if (te->errcode == TFTP_ERR_UNKNOWN_ERROR) { |
---|
107 | tftp_string_error_message[TFTP_ERR_UNKNOWN_ERROR]=strdup(te->errmsg); |
---|
108 | } |
---|
109 | err=-ntohs(te->errcode); // Return the associated error code |
---|
110 | goto done; |
---|
111 | } |
---|
112 | } |
---|
113 | } while ((clock_t)(times(NULL) - start) < *timeout); |
---|
114 | } |
---|
115 | |
---|
116 | done: |
---|
117 | lfree(ur); |
---|
118 | lfree(uw); |
---|
119 | |
---|
120 | return err; |
---|
121 | } |
---|
122 | |
---|
123 | static int upload_tftp_write(struct upload_backend *be) |
---|
124 | { |
---|
125 | static uint16_t local_port = 0x4000; |
---|
126 | struct tftp_state tftp; |
---|
127 | char buffer[512+4+6]; |
---|
128 | int nlen; |
---|
129 | int err=TFTP_OK; |
---|
130 | const union syslinux_derivative_info *sdi = |
---|
131 | syslinux_derivative_info(); |
---|
132 | const char *data = be->outbuf; |
---|
133 | size_t len = be->zbytes; |
---|
134 | size_t chunk; |
---|
135 | |
---|
136 | tftp.my_ip = sdi->pxe.myip; |
---|
137 | tftp.my_port = htons(local_port++); |
---|
138 | tftp.srv_gw = ((tftp.srv_ip ^ tftp.my_ip) & sdi->pxe.ipinfo->netmask) |
---|
139 | ? sdi->pxe.ipinfo->gateway : 0; |
---|
140 | tftp.srv_port = 0; |
---|
141 | tftp.seq = 0; |
---|
142 | |
---|
143 | if (be->argv[1]) { |
---|
144 | tftp.srv_ip = pxe_dns(be->argv[1]); |
---|
145 | if (!tftp.srv_ip) { |
---|
146 | // printf("\nUnable to resolve hostname: %s\n", be->argv[1]); |
---|
147 | return -TFTP_ERR_UNABLE_TO_RESOLVE; |
---|
148 | } |
---|
149 | } else { |
---|
150 | tftp.srv_ip = sdi->pxe.ipinfo->serverip; |
---|
151 | if (!tftp.srv_ip) { |
---|
152 | // printf("\nNo server IP address\n"); |
---|
153 | return -TFTP_ERR_UNABLE_TO_CONNECT; |
---|
154 | } |
---|
155 | } |
---|
156 | |
---|
157 | /* printf("server %u.%u.%u.%u... ", |
---|
158 | ((uint8_t *)&tftp.srv_ip)[0], |
---|
159 | ((uint8_t *)&tftp.srv_ip)[1], |
---|
160 | ((uint8_t *)&tftp.srv_ip)[2], |
---|
161 | ((uint8_t *)&tftp.srv_ip)[3]);*/ |
---|
162 | |
---|
163 | buffer[0] = 0; |
---|
164 | buffer[1] = TFTP_WRQ; |
---|
165 | nlen = strlcpy(buffer+2, be->argv[0], 512); |
---|
166 | memcpy(buffer+3+nlen, "octet", 6); |
---|
167 | |
---|
168 | if ((err=send_ack_packet(&tftp, buffer, 2+nlen+1+6))!=TFTP_OK) |
---|
169 | return err; |
---|
170 | |
---|
171 | do { |
---|
172 | chunk = len >= 512 ? 512 : len; |
---|
173 | |
---|
174 | buffer[1] = TFTP_DATA; |
---|
175 | *((uint16_t *)(buffer+2)) = htons(++tftp.seq); |
---|
176 | memcpy(buffer+4, data, chunk); |
---|
177 | data += chunk; |
---|
178 | len -= chunk; |
---|
179 | |
---|
180 | if ((err=send_ack_packet(&tftp, buffer, chunk+4))!=TFTP_OK) |
---|
181 | return err; |
---|
182 | } while (chunk == 512); |
---|
183 | |
---|
184 | return TFTP_OK; |
---|
185 | } |
---|
186 | |
---|
187 | struct upload_backend upload_tftp = { |
---|
188 | .name = "tftp", |
---|
189 | .helpmsg = "filename [tftp_server]", |
---|
190 | .minargs = 1, |
---|
191 | .write = upload_tftp_write, |
---|
192 | }; |
---|