1 /* tftp_send.c: the sending routine */
2
3 /*
4 * Copyright (C) 1999 Uwe Ohse
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "config.h"
22 #include <sys/types.h>
23 #include <sys/ioctl.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <signal.h>
27
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include "no_tftp.h"
31
32 #include <syslog.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include "timselsysdep.h"
37 #include "str2num.h"
38 #include "uostr.h"
39 #include "tftplib.h"
40
41 #define ERR_LOG1(x) do { const char *er=strerror(safe_errno); syslog(LOG_ERR,x,er); } while(1)
42 #define ERR_LOG2(x,y) do { const char *er=strerror(safe_errno); syslog(LOG_ERR,x,y,er); } while(1)
43 #define ERR_LOG3(x,y,z) do { const char *er=strerror(safe_errno); syslog(LOG_ERR,x,y,z,er); } while(1)
44 #define ES(x) do {int safe_errno=errno; { x } errno=safe_errno; } while(0)
45
46 static int got_sig;
47 static void
sig_handler(int signo)48 sig_handler(int signo)
49 {
50 got_sig=signo;
51 }
52
53 static ssize_t
my_read(int fd,char * buf,size_t len)54 my_read(int fd, char *buf,size_t len)
55 {
56 ssize_t got=0;
57 while (len) {
58 ssize_t t;
59 t=read(fd,buf,len);
60 if (t==-1 && errno==EINTR) continue;
61 if (t==-1) return -1;
62 if (t==0) break;
63 buf+=t;
64 len-=t;
65 got+=t;
66 }
67 return got;
68 }
69
70
71 int
tftp_send(const char * server,int port,const char * remotefilename,const char * localfilename,struct tftplib_ctrl * flags)72 tftp_send(const char *server, int port, const char *remotefilename, const char *localfilename, struct tftplib_ctrl *flags)
73 {
74 struct sockaddr_in s_in;
75 int remotefd;
76 int fd;
77 int blockno;
78 int lastchar=0; /* in case of netascii */
79 int may_get_oack=0;
80 const char *errortext;
81 struct sigaction sa,sa_old;
82 int got_timeouts=0;
83 size_t sendlength;
84 int is_connected=0;
85 int retcode=UTFTP_EC_UNDEF; /* undefined error */
86
87
88 memset (&s_in, 0, sizeof (s_in));
89 s_in.sin_family = AF_INET;
90 remotefd = socket (AF_INET, SOCK_DGRAM, 0);
91 if (remotefd < 0) { ES(syslog(LOG_ERR,"socket: %s",strerror(errno));); retcode=UTFTP_EC_LOCAL; goto err0; }
92 if (bind (remotefd, (struct sockaddr *) &s_in, sizeof (s_in)) < 0)
93 { ES(syslog(LOG_ERR,"bind: %s",strerror(errno));); retcode=UTFTP_EC_LOCAL; goto err1; }
94
95 fd=open(localfilename,O_RDONLY);
96 if (fd==-1) {
97 int e=errno;
98 const char *cs=strerror(errno);
99 syslog(LOG_ERR,"open(%s): %s",localfilename, cs);
100 if (e==ENOENT) tftp_nak(remotefd,ENOTFOUND,cs,flags);
101 if (e==EACCES) tftp_nak(remotefd,EACCESS,cs,flags);
102 else tftp_nak(remotefd,EUNDEF,cs,flags);
103 errno=e;
104 return 1;
105 }
106
107 sa.sa_handler = sig_handler;
108 sa.sa_flags = 0;
109 sigemptyset (&sa.sa_mask);
110 sigaction (SIGALRM, &sa, &sa_old);
111
112 sendlength=tftp_prepare_rq(WRQ, server, port, remotefilename, &s_in, &may_get_oack, flags);
113 if (sendlength==0) goto err2;
114
115 blockno=0;
116 while (1) {
117 ssize_t len;
118 /* get data */
119 if (sendlength) {
120 len=sendlength;
121 sendlength=0;
122 } else {
123 if (flags->netascii) {
124 char c;
125 /* LF -> CR LF, CR -> CR nul .... what a stupid thing :-) */
126 for (len=0;len<(ssize_t) flags->segsize;len++) {
127 if (lastchar=='\r') { c=0; lastchar = 0; }
128 else if (lastchar =='\n') { c='\n'; lastchar = 0; }
129 else {
130 ssize_t got;
131 got=my_read(fd,&c,1);
132 if (got==-1) {
133 errortext=strerror(errno);
134 ES( syslog(LOG_ERR,"read(%s): %s",localfilename,errortext););
135 retcode=UTFTP_EC_LOCAL;
136 goto do_error_nak;
137 }
138 if (got==0) break;
139 lastchar=c;
140 if (c=='\n') c='\r';
141 }
142 flags->sendbuf.buf[TFTP_OFFSET+len]=c;
143 }
144 } else {
145 len=my_read(fd,flags->sendbuf.buf+TFTP_OFFSET,flags->segsize);
146 if (len==-1) {
147 errortext=strerror(errno);
148 ES(syslog(LOG_ERR,"read(%s): %s",localfilename,errortext););
149 retcode=UTFTP_EC_LOCAL;
150 goto do_error_nak;
151 }
152 }
153 flags->sendbuf.hdr->th_opcode = htons((u_short)DATA);
154 flags->sendbuf.hdr->th_block = htons((u_short)blockno);
155 len+=TFTP_OFFSET;
156 }
157 while (1) {
158 struct tftphdr *rhdr;
159 ssize_t got;
160 /* send to the peer */
161 if (is_connected) {
162 if (send(remotefd, flags->sendbuf.buf, len, 0) != len) {
163 errortext=strerror(errno);
164 ES(syslog(LOG_ERR,"send(): %s",errortext););
165 retcode=UTFTP_EC_NETWORK;
166 goto err2;
167 }
168 } else {
169 if (sendto(remotefd, flags->sendbuf.buf, len, 0,(struct sockaddr *)&s_in,sizeof(s_in)) != len) {
170 errortext=strerror(errno);
171 ES(syslog(LOG_ERR,"send(): %s",errortext););
172 retcode=UTFTP_EC_NETWORK;
173 goto err2;
174 }
175 }
176 alarm(flags->timeout);
177 if (is_connected)
178 got = recv(remotefd, flags->recvbuf.buf, flags->segsize+TFTP_OFFSET, 0);
179 else {
180 socklen_t sl=sizeof(s_in);
181 got = recvfrom(remotefd, flags->recvbuf.buf, flags->segsize+TFTP_OFFSET, 0,(struct sockaddr *)&s_in,&sl);
182 if (got>=0) {
183 connect(remotefd,(struct sockaddr *)&s_in,sl);
184 is_connected=1;
185 }
186 }
187 if (got < 0) {
188 if (errno==EINTR && ++got_timeouts<flags->retries) continue;
189 ES(syslog(LOG_ERR, "recv: %s",strerror(errno)););
190 retcode=UTFTP_EC_TIMEOUT;
191 goto err2;
192 }
193 got_timeouts=0;
194
195 rhdr=(struct tftphdr *)flags->recvbuf.hdr;
196 rhdr->th_opcode = ntohs((u_short)rhdr->th_opcode);
197 rhdr->th_block = ntohs((u_short)rhdr->th_block);
198 if (rhdr->th_opcode == ERROR) {
199 ES(syslog(LOG_ERR, "got ERROR"););
200 retcode=UTFTP_EC_ERROR;
201 errortext="got ERROR";
202 goto err2;
203 }
204 if (rhdr->th_opcode==OACK) {
205 char *p;
206 if (!may_get_oack) {
207 errortext="got unwanted OACK opcode";ES(syslog(LOG_ERR,errortext););
208 retcode=UTFTP_EC_PROTO;
209 goto do_error_nak;
210 }
211 if (rhdr->th_block!=0) {
212 errortext="got OACK opcode at the wrong time";
213 ES(syslog(LOG_ERR,errortext););
214 retcode=UTFTP_EC_PROTO;
215 goto do_error_nak;
216 }
217 /* we need to process the options here. Server may use a lower blksize ... */
218 p=flags->recvbuf.buf+TFTP_OFFSET;
219 while (p!=flags->recvbuf.buf+got) {
220 char *opt;
221 char *val;
222 opt=p;
223 /* proceed to end of option name */
224 while (p!=flags->recvbuf.buf+got && *p!=0) p++;
225 if (p==flags->recvbuf.buf+got) {
226 errortext="unterminated option name in OACK packet";
227 syslog(LOG_ERR,errortext);
228 retcode=UTFTP_EC_PROTO;
229 goto do_error_nak;
230 }
231 val=p+1;
232 p=val;
233 /* proceed to end of option name */
234 while (p!=flags->recvbuf.buf+got && *p!=0) p++;
235 if (p==flags->recvbuf.buf+got) {
236 errortext="unterminated option value in OACK packet";
237 syslog(LOG_ERR,errortext);
238 retcode=UTFTP_EC_PROTO;
239 goto do_error_nak;
240 }
241 p++;
242 if (0==strcasecmp(opt,"blksize")) {
243 unsigned long x;
244 if (-1==str2ulong(val,&x,0)) {
245 errortext="received blksize is not a valid unsigned long";
246 syslog(LOG_ERR,"received blksize %s is not a valid unsigned long",val);
247 retcode=UTFTP_EC_PROTO; goto do_error_nak;
248 }
249 if (x<8) {
250 errortext="received blksize is too low";
251 syslog(LOG_ERR,"received blksize %s is too low",val);
252 retcode=UTFTP_EC_PROTO; goto do_error_nak;
253 }
254 if (x>flags->segsize) {
255 errortext="received blksize is too high";
256 syslog(LOG_ERR,"received blksize %s is higher then we wanted",val);
257 retcode=UTFTP_EC_PROTO; goto do_error_nak;
258 }
259 flags->segsize=x;
260 }
261 }
262 blockno=0;
263 got=TFTP_OFFSET;
264 may_get_oack=0;
265 break;
266 }
267
268 if (rhdr->th_opcode!=ACK) {
269 errortext="unknown TFTP opcode";
270 ES(syslog(LOG_ERR, "got unknown opcode %d",rhdr->th_opcode););
271 retcode=UTFTP_EC_PROTO; goto do_error_nak;
272 }
273
274 if (rhdr->th_block == blockno) { /* ACK for our block */
275 if (flags->force_timeout) {
276 int o=alarm(0);
277 sleep(flags->force_timeout);
278 alarm(o);
279 }
280 if (flags->force_stop) {
281 close(fd);
282 return 0;
283 }
284 break;
285 }
286
287 /* ACK for something else. Throw away everything the kernel may have buffered */
288 while (1) {
289 struct timeval tv;
290 fd_set set;
291 FD_SET(remotefd,&set);
292 tv.tv_sec=0;
293 tv.tv_usec=0;
294 if (select(remotefd+1,&set,0,0,&tv)<1)
295 break;
296 (void) recv(remotefd, flags->recvbuf.buf, flags->segsize +TFTP_OFFSET, 0);
297 }
298 /* we will now simply resend the block */
299 } /* while !ACK for our block */
300 if (len-TFTP_OFFSET!=(ssize_t) flags->segsize && blockno)
301 break;
302 blockno++;
303 }
304 return 0;
305 do_error_nak:
306 ES(
307 tftp_nak(remotefd,EUNDEF,errortext,flags);
308 );
309 err2: close(fd); alarm(0); sigaction(SIGALRM,&sa_old,0);
310 err1: close(remotefd);
311 err0:
312 return retcode;
313 }
314