1 /* utftpd_recv.c: the server 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 
31 #include <syslog.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <signal.h>
36 #include "timselsysdep.h"
37 #include "str2num.h"
38 #include "uostr.h"
39 #include "uoio.h"
40 #include "utftpd.h"
41 
42 #define ERR_LOG1(x) do { const char *er=strerror(errno); syslog(LOG_ERR,x,er); } while(0)
43 #define ERR_LOG2(x,y) do { const char *er=strerror(errno); syslog(LOG_ERR,x,y,er); } while(0)
44 
45 static int got_sig;
46 static void
47 sig_handler(int signo)
48 {
49 	got_sig=signo;
50 }
51 
52 /*
53  * well, this is everything but beautiful. To be able to resend a lost packet we
54  * have to send the first ACK/OACK here. But since that contains data we should not
55  * now about ...
56  * i absolutely don't care about any cleanup actions here: we exit after we received
57  * that file.
58  */
59 int
60 utftpd_recv(struct utftpd_ctrl *flags)
61 {
62 	uoio_t io;
63 	int blockno;
64 	int lastchar=-1; /* in case of netascii */
65 	int netascii=flags->netascii;
66 	ssize_t got;
67 	int got_timeouts=0;
68 	struct sigaction sa;
69 	struct sigaction old_sa;
70 	const char *errortext;
71 	size_t sendlength;
72 	unsigned short got_blockno;
73 	short got_opcode;
74 	int is_final=0;
75 
76 	sa.sa_handler = sig_handler;
77 	sa.sa_flags = 0;
78 	sigemptyset (&sa.sa_mask);
79 	sigaction (SIGALRM, &sa, &old_sa);
80 
81 	uoio_assign_w(&io,flags->filefd,write,0);
82 
83 	blockno=1;
84 	got=flags->segsize+TFTP_OFFSET;
85 	flags->sendbuf.hdr->th_block = htons(0);
86 	sendlength=flags->first_packet_length+TFTP_OFFSET;
87 
88 	while (1) {
89 		/* send the ACK / OACK */
90   		while (1) {
91 			if (send(flags->remotefd, flags->sendbuf.buf, sendlength, 0) != (ssize_t) sendlength)
92 				{ ERR_LOG1("send: %s"); goto do_error; }
93 
94 			/* read data */
95 			alarm( (flags->timeout ? flags->timeout : 5)
96 				* (got_timeouts+1));
97 			got = recv(flags->remotefd, flags->recvbuf.buf, flags->segsize+TFTP_OFFSET, 0);
98 			if (got < 0) {
99 				if (errno==EINTR && got_sig==SIGALRM && got_timeouts++<5) continue; /* resend my ACK */
100 				ERR_LOG1("recv: %s");
101 				goto do_error;
102 			}
103 			got_timeouts=0; /* we got a packet */
104 			got_opcode = ntohs((u_short)flags->recvbuf.hdr->th_opcode);
105 			got_blockno = ntohs((u_short)flags->recvbuf.hdr->th_block);
106 			if (got_opcode == ERROR) {
107 				if (got>5) {
108 					if (flags->recvbuf.buf[got-1]!=0) {
109 						syslog(LOG_ERR,"got ERROR opcode with unterminated error text, last character removed:");
110 						flags->recvbuf.buf[got-1]=0;
111 					}
112 					syslog(LOG_ERR,"%s",flags->recvbuf.buf+TFTP_OFFSET);
113 					goto do_error;
114 				} else {
115 					syslog(LOG_ERR,"got ERROR opcode, no error text");
116 					goto do_error;
117 				}
118 			}
119 			if (got_opcode != DATA) {
120 				syslog(LOG_ERR, "got unknown opcode %d",flags->recvbuf.hdr->th_opcode);
121 				errortext="got unknown TFTP opcode";
122 				goto do_error_nak;
123 			}
124 			if (got_blockno == blockno) break;
125 			/* out of sync. Throw away everything the kernel may have buffered */
126 			while (1) {
127 				struct timeval tv;
128 				fd_set set;
129 				FD_SET(flags->remotefd,&set);
130 				tv.tv_sec=0;
131 				tv.tv_usec=0;
132 				if (select(flags->remotefd+1,&set,0,0,&tv)<1)
133 					break;
134 				alarm(flags->timeout ? flags->timeout : 5);
135 				(void) recv(flags->remotefd, flags->recvbuf.buf, flags->segsize +TFTP_OFFSET, 0);
136 			}
137 			/* go back to resend ACK */
138 		}
139 		/* ok, got something */
140 		if (netascii) {
141 			char *p,*q,*end;
142 			p=q=flags->recvbuf.buf+TFTP_OFFSET;
143 			end=flags->recvbuf.buf+got;
144 			while (p!=end) {
145 				if (lastchar=='\r') {
146 					if (*p==0) {
147 						*q++='\r';
148 						p++;
149 					} else
150 						*q++=*p++;
151 					lastchar=-1;
152 				} else {
153 					lastchar=*p;
154 					if (*p=='\r')
155 						p++;
156 					else
157 						*q++=*p++;
158 				}
159 			}
160 			uoio_write_mem(&io,flags->recvbuf.buf+TFTP_OFFSET,q-(flags->recvbuf.buf+TFTP_OFFSET));
161 			flags->bytes+=q-(flags->recvbuf.buf+TFTP_OFFSET);
162 		} else {
163 			uoio_write_mem(&io,flags->recvbuf.buf+TFTP_OFFSET,got-TFTP_OFFSET);
164 			flags->bytes+=got-TFTP_OFFSET;
165 		}
166 		if (got!=(ssize_t) flags->segsize+TFTP_OFFSET) {
167 			uoio_flush(&io);
168 			uoio_destroy(&io);
169 #if 0
170 #ifdef HAVE_FSYNC
171 			if (-1==fsync(flags->filefd)) { errortext=strerror(errno); ERR_LOG2("fsync(%s): %s", flags->filename); goto do_error_nak; }
172 #else
173 			sync();
174 #endif
175 			if (-1==close(flags->filefd)) { errortext=strerror(errno); ERR_LOG2("close(%s): %s", flags->filename); goto do_error_nak; }
176 #endif
177 			is_final=1;
178 		}
179 		flags->sendbuf.hdr->th_opcode = htons((u_short)ACK);
180 		flags->sendbuf.hdr->th_block = htons((u_short)blockno++);
181 		sendlength=TFTP_OFFSET;
182 		if (is_final) break; /* final ACK is sent from above, after version control */
183 	} /* while 1 */
184 	alarm(0);
185 	sigaction (SIGALRM, &old_sa,0 );
186 	return 0;
187   do_error_nak:
188 	alarm(0);
189 	sigaction (SIGALRM, &old_sa,0 );
190 	utftpd_nak(flags->remotefd,EUNDEF,errortext,flags);
191 	return -1;
192   do_error:
193 	alarm(0);
194 	sigaction (SIGALRM, &old_sa,0 );
195 	return -1;
196 }
197