6d35e1704f6e6dccc37f87dd280a1da097032161
[people/lynusvaz/gpxe.git] / src / proto / tftp.c
1 #include "etherboot.h"
2 #include "in.h"
3 #include "nic.h"
4 #include "proto.h"
5 #include "tftp.h"
6
7 /* Utility function for tftp_block() */
8 static int await_tftp ( int ival, void *ptr __unused,
9                         unsigned short ptype __unused, struct iphdr *ip,
10                         struct udphdr *udp, struct tcphdr *tcp __unused ) {
11         if ( ! udp ) {
12                 return 0;
13         }
14         if ( arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr )
15                 return 0;
16         if ( ntohs ( udp->dest ) != ival )
17                 return 0;
18         return 1;
19 }
20
21 /*
22  * Download a single block via TFTP.  This function is non-static so
23  * that pxe_export.c can call it.
24  *
25  */
26 int tftp_block ( struct tftpreq_info_t *request,
27                  struct tftpblk_info_t *block ) {
28         static struct sockaddr_in server;
29         static unsigned short lport = 2000; /* local port */
30         struct tftp_t *rcvd = NULL;
31         static struct tftpreq_t xmit;
32         static unsigned short xmitlen = 0;
33         static unsigned short blockidx = 0; /* Last block received */
34         static unsigned short retry = 0; /* Retry attempts on last block */
35         static int blksize = 0;
36         unsigned short recvlen = 0;
37
38         /* If this is a new request (i.e. if name is set), fill in
39          * transmit block with RRQ and send it.
40          */
41         if ( request ) {
42                 rx_qdrain(); /* Flush receive queue */
43                 xmit.opcode = htons(TFTP_RRQ);
44                 xmitlen = (void*)&xmit.u.rrq - (void*)&xmit +
45                         sprintf((char*)xmit.u.rrq, "%s%coctet%cblksize%c%d",
46                                 request->name, 0, 0, 0, request->blksize)
47                         + 1; /* null terminator */
48                 blockidx = 0; /* Reset counters */
49                 retry = 0;
50                 blksize = TFTP_DEFAULTSIZE_PACKET;
51                 lport++; /* Use new local port */
52                 server = *(request->server);
53                 if ( !udp_transmit(server.sin_addr.s_addr, lport,
54                                    server.sin_port, xmitlen, &xmit) )
55                         return (0);
56         }
57         /* Exit if no transfer in progress */
58         if ( !blksize ) return (0);
59         /* Loop to wait until we get a packet we're interested in */
60         block->data = NULL; /* Used as flag */
61         while ( block->data == NULL ) {
62                 long timeout = rfc2131_sleep_interval ( blockidx ? TFTP_REXMT :
63                                                         TIMEOUT, retry );
64                 if ( !await_reply(await_tftp, lport, NULL, timeout) ) {
65                         /* No packet received */
66                         if ( retry++ > MAX_TFTP_RETRIES ) break;
67                         /* Retransmit last packet */
68                         if ( !blockidx ) lport++; /* New lport if new RRQ */
69                         if ( !udp_transmit(server.sin_addr.s_addr, lport,
70                                            server.sin_port, xmitlen, &xmit) )
71                                 return (0);
72                         continue; /* Back to waiting for packet */
73                 }
74                 /* Packet has been received */
75                 rcvd = (struct tftp_t *)&nic.packet[ETH_HLEN];
76                 recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr)
77                         - sizeof(rcvd->opcode);
78                 server.sin_port = ntohs(rcvd->udp.src);
79                 retry = 0; /* Reset retry counter */
80                 switch ( htons(rcvd->opcode) ) {
81                 case TFTP_ERROR : {
82                         printf ( "TFTP error %d (%s)\n",
83                                  ntohs(rcvd->u.err.errcode),
84                                  rcvd->u.err.errmsg );
85                         return (0); /* abort */
86                 }
87                 case TFTP_OACK : {
88                         const char *p = rcvd->u.oack.data;
89                         const char *e = p + recvlen - 10; /* "blksize\0\d\0" */
90
91                         *((char*)(p+recvlen-1)) = '\0'; /* Force final 0 */
92                         if ( blockidx || !request ) break; /* Too late */
93                         if ( recvlen <= TFTP_MAX_PACKET ) /* sanity */ {
94                                 /* Check for blksize option honoured */
95                                 while ( p < e ) {
96                                         if ( strcasecmp("blksize",p) == 0 &&
97                                              p[7] == '\0' ) {
98                                                 blksize = strtoul(p+8,&p,10);
99                                                 p++; /* skip null */
100                                         }
101                                         while ( *(p++) ) {};
102                                 }
103                         }
104                         if ( blksize < TFTP_DEFAULTSIZE_PACKET ||
105                              blksize > request->blksize ) {
106                                 /* Incorrect blksize - error and abort */
107                                 xmit.opcode = htons(TFTP_ERROR);
108                                 xmit.u.err.errcode = 8;
109                                 xmitlen = (void*)&xmit.u.err.errmsg
110                                         - (void*)&xmit
111                                         + sprintf((char*)xmit.u.err.errmsg,
112                                                   "RFC1782 error")
113                                         + 1;
114                                 udp_transmit(server.sin_addr.s_addr, lport,
115                                              server.sin_port, xmitlen, &xmit);
116                                 return (0);
117                         }
118                 } break;
119                 case TFTP_DATA :
120                         if ( ntohs(rcvd->u.data.block) != ( blockidx + 1 ) )
121                                 break; /* Re-ACK last block sent */
122                         if ( recvlen > ( blksize+sizeof(rcvd->u.data.block) ) )
123                                 break; /* Too large; ignore */
124                         block->data = rcvd->u.data.download;
125                         block->block = ++blockidx;
126                         block->len = recvlen - sizeof(rcvd->u.data.block);
127                         block->eof = ( (unsigned short)block->len < blksize );
128                         /* If EOF, zero blksize to indicate transfer done */
129                         if ( block->eof ) blksize = 0;
130                         break;
131                 default: break; /* Do nothing */
132                 }
133                 /* Send ACK */
134                 xmit.opcode = htons(TFTP_ACK);
135                 xmit.u.ack.block = htons(blockidx);
136                 xmitlen = TFTP_MIN_PACKET;
137                 udp_transmit ( server.sin_addr.s_addr, lport, server.sin_port,
138                                xmitlen, &xmit );
139         }
140         return ( block->data ? 1 : 0 );
141 }
142
143 /*
144  * Download a file via TFTP
145  *
146  */
147 int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
148            struct buffer *buffer ) {
149         struct tftpreq_info_t request_data = {
150                 .server = server,
151                 .name = file,
152                 .blksize = TFTP_MAX_PACKET,
153         };
154         struct tftpreq_info_t *request = &request_data;
155         struct tftpblk_info_t block;
156         off_t offset = 0;
157
158         do {
159                 if ( ! tftp_block ( request, &block ) )
160                         return 0;
161                 if ( ! fill_buffer ( buffer, block.data, offset, block.len ) )
162                         return 0;
163                 offset += block.len;
164                 request = NULL; /* Send request only once */
165         } while ( ! block.eof );
166
167         return 1;
168 }
169
170 struct protocol tftp_protocol __default_protocol = {
171         .name = "tftp",
172         .default_port = TFTP_PORT,
173         .load = tftp,
174 };