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