source: bootcd/isolinux/syslinux-6.03/com32/libupload/upload_tftp.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 4.5 KB
Line 
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
13enum tftp_opcode {
14    TFTP_RRQ    = 1,
15    TFTP_WRQ    = 2,
16    TFTP_DATA   = 3,
17    TFTP_ACK    = 4,
18    TFTP_ERROR  = 5,
19};
20
21struct tftp_error {
22        uint16_t opcode;
23        uint16_t errcode;
24        char errmsg[0];
25} __attribute__ (( packed ));
26
27struct 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
36const 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
53static 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
116done:
117    lfree(ur);
118    lfree(uw);
119
120    return err;
121}
122
123static 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
187struct upload_backend upload_tftp = {
188    .name       = "tftp",
189    .helpmsg    = "filename [tftp_server]",
190    .minargs    = 1,
191    .write      = upload_tftp_write,
192};
Note: See TracBrowser for help on using the repository browser.