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