1 /* tftp_receive.c: client side receiving 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 
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include "no_tftp.h"
30 #include <netdb.h>
31 
32 #include <syslog.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <signal.h>
37 #include "timselsysdep.h"
38 #include "str_ulong.h"
39 #include "str2num.h"
40 #include "tftplib.h"
41 
42 #define ERR_LOG1(x) do { const char *er=strerror(safe_errno); syslog(LOG_ERR,x,er); } while(0)
43 #define ERR_LOG2(x,y) do { const char *er=strerror(safe_errno); syslog(LOG_ERR,x,y,er); } while(0)
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 int
my_write(int fd,char * buf,size_t size)54 my_write(int fd, char *buf,size_t size)
55 {
56 	while (size) {
57 		ssize_t got;
58 		got=write(fd,buf,size);
59 		if (-1==got) {
60 			if (errno!=EINTR) return -1;
61 		} else {
62 			buf+=got;
63 			size-=got;
64 		}
65 	}
66 	return 0;
67 }
68 
69 /*
70  *
71  */
72 int
tftp_receive(const char * remotehost,int port,const char * remotefilename,const char * localfilename,struct tftplib_ctrl * flags)73 tftp_receive(const char *remotehost, int port, const char *remotefilename, const char *localfilename, struct tftplib_ctrl *flags)
74 {
75     struct sockaddr_in s_in;
76     int remotefd;
77 	int blockno;
78 	int lastchar=-1; /* in case of netascii */
79 	ssize_t got;
80 	int fd;
81 	int got_timeout=0;
82 	struct sigaction sa;
83 	struct sigaction old_sa;
84 	const char *errortext;
85 	int is_connected=0;
86 	int may_get_oack=0;
87 	size_t sendlength;
88 	int is_final=0;
89 	short got_opcode=-1; /* get rid of warning */
90 	short got_blockno;
91 	int retcode=UTFTP_EC_UNDEF; /* undefined errror */
92 
93 
94     memset (&s_in, 0, sizeof (s_in));
95     s_in.sin_family = AF_INET;
96     remotefd = socket (AF_INET, SOCK_DGRAM, 0);
97     if (remotefd < 0) { ES(syslog(LOG_ERR,"socket: %s",strerror(errno));); retcode=UTFTP_EC_LOCAL; goto err0; }
98     if (bind (remotefd, (struct sockaddr *) &s_in, sizeof (s_in)) < 0)
99         { ES(syslog(LOG_ERR,"bind: %s",strerror(errno));); retcode=UTFTP_EC_LOCAL; goto err1; }
100 
101 	fd=open(localfilename,O_WRONLY|O_CREAT|O_TRUNC
102 #if !defined(HAVE_FSYNC) && defined(O_SYNC)
103 		|O_SYNC
104 #endif
105 		,0666);
106 	if (fd==-1) {
107 		ES(
108 			const char *cs=strerror(safe_errno);
109 			syslog(LOG_ERR,"open(%s): %s",localfilename, cs);
110 		);
111 		retcode=UTFTP_EC_LOCAL;
112 		goto err1;
113 	}
114 
115 	sa.sa_handler = sig_handler;
116 	sa.sa_flags = 0;
117 	sigemptyset (&sa.sa_mask);
118 	sigaction (SIGALRM, &sa, &old_sa);
119 	blockno=1; /* we expect this */
120 	got=flags->segsize+TFTP_OFFSET;
121 
122 	sendlength=tftp_prepare_rq(RRQ, remotehost, port, remotefilename, &s_in, &may_get_oack, flags);
123 	if (sendlength==0) goto err2; /* undefined error */
124 
125 	while (1) {
126 		/* flags->sendbuf contains to data to be sent.
127 		 * sendlength is the number of bytes to send.
128 		 */
129   		while (1) {
130 			if (is_connected) {
131 				if (send (remotefd, flags->sendbuf.buf, sendlength, 0)
132 					!= (ssize_t) sendlength) {
133 					ES (ERR_LOG1 ("send: %s"););
134 					retcode = UTFTP_EC_NETWORK;
135 					goto err3;
136 				}
137 				if (flags->duplicate_ack) {
138 					if (send (remotefd, flags->sendbuf.buf, sendlength, 0)
139 						!= (ssize_t) sendlength) {
140 						ES (ERR_LOG1 ("send: %s"););
141 						retcode = UTFTP_EC_NETWORK;
142 						goto err3;
143 					}
144 				}
145 			} else {
146 				if (sendto(remotefd, flags->sendbuf.buf, sendlength, 0,(struct sockaddr *)&s_in,sizeof(s_in))
147 					!= (ssize_t) sendlength)
148 						{ ES(ERR_LOG1("send: %s");); retcode=UTFTP_EC_NETWORK; goto err3; }
149 			}
150 			if (is_final) break;
151 
152 			/* read data */
153 			alarm(flags->timeout ? flags->timeout : 5);
154 			if (is_connected) {
155 				got = recv(remotefd, flags->recvbuf.buf, flags->segsize+TFTP_OFFSET, 0);
156 			} else {
157 				socklen_t s_len=sizeof(s_in);
158 				got = recvfrom(remotefd, flags->recvbuf.buf, flags->segsize+TFTP_OFFSET, 0, (struct sockaddr *)&s_in,&s_len);
159 				if (got>=0) {
160 					if (0!= connect(remotefd,(struct sockaddr *)&s_in,sizeof(s_in)))
161 							{ ES(syslog(LOG_ERR,"connect: %s",strerror(errno));); retcode=UTFTP_EC_NETWORK; goto err3; }
162 					is_connected=1;
163 				}
164 			}
165 			if (got < 0) {
166 				if (errno==EINTR && got_sig==SIGALRM) {
167 					if (++got_timeout<flags->retries) continue; /* resend my ACK */
168 				}
169 				ES(ERR_LOG1("recv: %s");); retcode=UTFTP_EC_TIMEOUT; goto err3;
170 			}
171 			got_timeout=0; /* we got a packet */
172 			got_opcode = ntohs((u_short)flags->recvbuf.hdr->th_opcode);
173 			got_blockno = ntohs((u_short)flags->recvbuf.hdr->th_block);
174 			if (got_opcode == ERROR) {
175 				if (got>5) {
176 					if (flags->recvbuf.buf[got-1]!=0) {
177 						ES(syslog(LOG_ERR,"got ERROR opcode with unterminated error text, last character removed:"););
178 						flags->recvbuf.buf[got-1]=0;
179 					}
180 					ES(syslog(LOG_ERR,"%s",flags->recvbuf.buf+TFTP_OFFSET););
181 					retcode=UTFTP_EC_ERROR;
182 					goto err3;
183 				} else {
184 					ES(syslog(LOG_ERR,"got ERROR opcode, no error text");); retcode=UTFTP_EC_ERROR; goto err3;
185 				}
186 			}
187 			if (got_opcode == OACK) {
188 				char *p;
189 				if (!may_get_oack) { ES(syslog(LOG_ERR,"got unwanted OACK opcode");); retcode=UTFTP_EC_PROTO; goto err3; }
190 				if (blockno!=1) { ES(syslog(LOG_ERR,"got OACK opcode at the wrong time");); retcode=UTFTP_EC_PROTO; goto err3; }
191 				/* we need to process the options here. Server may use a lower blksize ... */
192 				p=flags->recvbuf.buf+TFTP_OFFSET;
193 				while (p!=flags->recvbuf.buf+got) {
194 					char *opt;
195 					char *val;
196 					opt=p;
197 					/* proceed to end of option name */
198 					while (p!=flags->recvbuf.buf+got && *p!=0) p++;
199 					if (p==flags->recvbuf.buf+got) {
200 						syslog(LOG_ERR,"unterminated option name in OACK packet");
201 						_exit(1);
202 					}
203 					val=p+1;
204 					p=val;
205 					/* proceed to end of option name */
206 					while (p!=flags->recvbuf.buf+got && *p!=0) p++;
207 					if (p==flags->recvbuf.buf+got) {
208 						syslog(LOG_ERR,"unterminated option value in OACK packet");
209 						retcode=UTFTP_EC_PROTO;
210 						goto err3;
211 					}
212 					p++;
213 					if (0==strcasecmp(opt,"blksize")) {
214 						unsigned long x;
215 						if (-1==str2ulong(val,&x,0)) {
216 							syslog(LOG_ERR,"received blksize %s is not a valid unsigned long",val);
217 							retcode=UTFTP_EC_PROTO;
218 							goto err3;
219 						}
220 						if (x<8) { syslog(LOG_ERR,"received blksize %s is too low",val); retcode=UTFTP_EC_PROTO; goto err3; }
221 						if (x>flags->segsize) {
222 							syslog(LOG_ERR,"received blksize %s is higher then we wanted",val); retcode=UTFTP_EC_PROTO; goto err3;
223 						}
224 						flags->segsize=x;
225 					}
226 				}
227 
228 				blockno=0;
229 				got=TFTP_OFFSET;
230 				may_get_oack=0;
231 				break;
232 			}
233 			if (got_opcode != DATA)  { ES(syslog(LOG_ERR, "got unknown opcode %d",flags->recvbuf.hdr->th_opcode);); retcode=UTFTP_EC_PROTO; goto err3; }
234 			if (got_blockno == blockno) {
235 				if (may_get_oack) {
236 					/* we should have gotten an OACK, but didn't get one.
237 					 * go back to defaults.
238 					 */
239 					flags=tftp_setup_ctrl(flags, 512);
240 					if (!flags) { ES(syslog(LOG_ERR,"out of memory");); retcode=1; retcode=UTFTP_EC_LOCAL; goto err3; }
241 				}
242 				break;
243 			}
244 			/* out of sync. Throw away everything the kernel may have buffered */
245 			while (1) {
246 				struct timeval tv;
247 				fd_set set;
248 				FD_SET(remotefd,&set);
249 				tv.tv_sec=0;
250 				tv.tv_usec=0;
251 				if (select(remotefd+1,&set,0,0,&tv)<1)
252 					break;
253 				alarm(flags->timeout ? flags->timeout : 5);
254 				(void) recv(remotefd, flags->recvbuf.buf, flags->segsize +TFTP_OFFSET, 0);
255 			}
256 			/* go back to resend packet */
257 		}
258 		if (is_final)
259 			break;
260 		/* ok, we got something. Safe it: */
261 		if (flags->netascii) {
262 			char *p,*q,*end;
263 			p=q=flags->recvbuf.buf+TFTP_OFFSET;
264 			end=flags->recvbuf.buf+got;
265 			while (p!=end) {
266 				if (lastchar=='\r') {
267 					if (*p==0) {
268 						*q++='\r';
269 						p++;
270 					} else
271 						*q++=*p++;
272 					lastchar=-1;
273 				} else {
274 					lastchar=*p;
275 					if (*p=='\r')
276 						p++;
277 					else
278 						*q++=*p++;
279 				}
280 			}
281 			if (-1==my_write(fd,flags->recvbuf.buf+TFTP_OFFSET,q-(flags->recvbuf.buf+TFTP_OFFSET))) {
282 				ES(ERR_LOG2("write(%s): %s", localfilename); errortext=strerror(safe_errno);); retcode=UTFTP_EC_LOCAL ; goto do_error_nak;
283 			}
284 		} else {
285 			if (-1==my_write(fd,flags->recvbuf.buf+TFTP_OFFSET,got-TFTP_OFFSET)) {
286 				ES(ERR_LOG2("write(%s): %s", localfilename); errortext=strerror(safe_errno);); retcode=UTFTP_EC_LOCAL ; goto do_error_nak;
287 			}
288 		}
289 		/* send an ACK */
290 		flags->sendbuf.hdr->th_opcode = htons((u_short)ACK);
291 		flags->sendbuf.hdr->th_block = htons((u_short)blockno++);
292 		if (flags->force_timeout) {
293 			int o=alarm(0);
294 			sleep(flags->force_timeout);
295 			alarm(o);
296 		}
297 		sendlength=TFTP_OFFSET;
298 		if (got_opcode==DATA && got!=(ssize_t) flags->segsize+TFTP_OFFSET) {
299 			/* send final ACK */
300 #ifdef HAVE_FSYNC
301 			if (-1==fsync(fd)) { ES(ERR_LOG2("fsync(%s): %s", localfilename); errortext=strerror(safe_errno););
302 				retcode=UTFTP_EC_LOCAL; goto do_error_nak; }
303 #else
304 # ifndef(O_SYNC)
305 			sync();
306 # endif
307 #endif
308 			if (-1==close(fd)) { ES(ERR_LOG2("close(%s): %s", localfilename); errortext=strerror(safe_errno););
309 				retcode=UTFTP_EC_LOCAL; goto do_error_nak; }
310 			is_final=1;
311 		}
312 	}
313 	alarm(0);
314 	sigaction (SIGALRM, &old_sa,0 );
315 	close(remotefd);
316 	return UTFTP_EC_OK;
317 do_error_nak:
318 	tftp_nak(remotefd,EUNDEF,errortext,flags);
319 err3:
320 	ES(
321 		alarm(0);
322 		sigaction (SIGALRM, &old_sa,0 );
323 	);
324 err2:
325 	close(fd);
326 err1:
327 	close(remotefd);
328 err0:
329 	return retcode;
330 }
331