1/*
2 * $Id: session.c,v 1.20 2009-10-16 01:10:59 didg Exp $
3 *
4 * Copyright (c) 1990,1994 Regents of The University of Michigan.
5 * All Rights Reserved.  See COPYRIGHT.
6 */
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif /* HAVE_CONFIG_H */
11
12#ifdef HAVE_SYS_ERRNO_H
13#include <sys/errno.h>
14#endif /* HAVE_SYS_ERRNO_H */
15#ifdef HAVE_ERRNO_H
16#include <errno.h>
17#endif /* HAVE_ERRNO_H */
18
19#include <stdlib.h>
20#include <string.h>
21#include <sys/types.h>
22#include <atalk/logger.h>
23#include <sys/time.h>
24#include <sys/uio.h>
25#include <netatalk/endian.h>
26#include <netatalk/at.h>
27#include <atalk/atp.h>
28#include <atalk/pap.h>
29
30#include "file.h"
31#include "lp.h"
32#include "session.h"
33
34int ps(struct papfile *infile, struct papfile *outfile, struct sockaddr_at *sat);
35
36extern unsigned char	connid, quantum, oquantum;
37
38static char		buf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
39static struct iovec	niov[ PAP_MAXQUANTUM ] = {
40    { buf[ 0 ],	0 },
41    { buf[ 1 ],	0 },
42    { buf[ 2 ],	0 },
43    { buf[ 3 ],	0 },
44    { buf[ 4 ],	0 },
45    { buf[ 5 ],	0 },
46    { buf[ 6 ],	0 },
47    { buf[ 7 ],	0 },
48};
49
50/*
51 * Accept files until the client closes the connection.
52 * Read lines of a file, until the client sends eof, after
53 * which we'll send eof also.
54 */
55int session(ATP atp, struct sockaddr_at *sat)
56{
57    struct timeval	tv;
58    struct atp_block	atpb;
59    struct sockaddr_at	ssat;
60    struct papfile	infile, outfile;
61    fd_set		fds;
62    char		cbuf[ 578 ];
63    int			i, cc, timeout = 0, readpending = 0;
64    u_int16_t		seq = 0, rseq = 1, netseq;
65    u_char		readport; /* uninitialized, OK 310105 */
66
67    infile.pf_state = PF_BOT;
68    infile.pf_bufsize = 0;
69    infile.pf_datalen = 0;
70    infile.pf_buf = NULL;
71    infile.pf_data = NULL;
72
73    outfile.pf_state = PF_BOT;
74    outfile.pf_bufsize = 0;
75    outfile.pf_datalen = 0;
76    outfile.pf_buf = NULL;
77    outfile.pf_data = NULL;
78
79    /*
80     * Ask for data.
81     */
82    cbuf[ 0 ] = connid;
83    cbuf[ 1 ] = PAP_READ;
84    if (++seq == 0) seq = 1;
85    netseq = htons( seq );
86    memcpy( &cbuf[ 2 ], &netseq, sizeof( netseq ));
87    atpb.atp_saddr = sat;
88    atpb.atp_sreqdata = cbuf;
89    atpb.atp_sreqdlen = 4;		/* bytes in SendData request */
90    atpb.atp_sreqto = 5;		/* retry timer */
91    atpb.atp_sreqtries = -1;		/* infinite retries */
92    if ( atp_sreq( atp, &atpb, oquantum, ATP_XO )) {
93	LOG(log_error, logtype_papd, "atp_sreq: %s", strerror(errno) );
94	return( -1 );
95    }
96
97    for (;;) {
98	/*
99	 * Time between tickles.
100	 */
101	tv.tv_sec = 60;
102	tv.tv_usec = 0;
103
104	/*
105	 * If we don't get anything for a while, time out.
106	 */
107	FD_ZERO( &fds );
108	FD_SET( atp_fileno( atp ), &fds );
109
110	do { /* do list until success or an unrecoverable error occurs */
111	  if (( cc = select( FD_SETSIZE, &fds, NULL, NULL, &tv )) < 0 )
112	      LOG(log_error, logtype_papd, "select: %s", strerror(errno) ); /* log all errors */
113	} while (( cc < 0 ) && (errno == 4));
114
115	if ( cc < 0 ) {
116	  LOG(log_error, logtype_papd, "select: Error is unrecoverable" );
117	  return( -1 );
118	}
119	if ( cc == 0 ) {
120	    if ( timeout++ > 2 ) {
121		LOG(log_error, logtype_papd, "connection timed out" );
122		lp_cancel();
123		return( -1 );
124	    }
125
126	    /*
127	     * Send a tickle.
128	     */
129	    cbuf[ 0 ] = connid;
130	    cbuf[ 1 ] = PAP_TICKLE;
131	    cbuf[ 2 ] = cbuf[ 3 ] = 0;
132	    atpb.atp_saddr = sat;
133	    atpb.atp_sreqdata = cbuf;
134	    atpb.atp_sreqdlen = 4;		/* bytes in Tickle request */
135	    atpb.atp_sreqto = 0;		/* best effort */
136	    atpb.atp_sreqtries = 1;		/* try once */
137	    if ( atp_sreq( atp, &atpb, 0, 0 )) {
138		LOG(log_error, logtype_papd, "atp_sreq: %s", strerror(errno) );
139		return( -1 );
140	    }
141	    continue;
142	} else {
143	    timeout = 0;
144	}
145
146	memset( &ssat, 0, sizeof( struct sockaddr_at ));
147	switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
148	case ATP_TREQ :
149	    atpb.atp_saddr = &ssat;
150	    atpb.atp_rreqdata = cbuf;
151	    atpb.atp_rreqdlen = sizeof( cbuf );
152	    if ( atp_rreq( atp, &atpb ) < 0 ) {
153		LOG(log_error, logtype_papd, "atp_rreq: %s", strerror(errno) );
154		return( -1 );
155	    }
156	    /* sanity */
157	    if ( (unsigned char)cbuf[ 0 ] != connid ) {
158		LOG(log_error, logtype_papd, "Bad ATP request!" );
159		continue;
160	    }
161
162	    switch( cbuf[ 1 ] ) {
163	    case PAP_READ :
164		/*
165		 * Other side is ready for some data.
166		 */
167		memcpy( &netseq, &cbuf[ 2 ], sizeof( netseq ));
168		if ( netseq != 0 ) {
169		    if ( rseq != ntohs( netseq )) {
170			break;
171		    }
172		    if ( rseq++ == 0xffff ) rseq = 1;
173		}
174		readpending = 1;
175		readport = ssat.sat_port;
176		break;
177
178	    case PAP_CLOSE :
179		/*
180		 * Respond to the close request.
181		 * If we're in the middle of a file, clean up.
182		 */
183		if (( infile.pf_state & PF_BOT ) ||
184			( infile.pf_datalen == 0 &&
185			( infile.pf_state & PF_EOF ))) {
186		    lp_print();
187		} else {
188		    lp_cancel();
189		}
190
191		niov[ 0 ].iov_len = 4;
192		((char *)niov[ 0 ].iov_base)[ 0 ] = connid;
193		((char *)niov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
194		((char *)niov[ 0 ].iov_base)[ 2 ] =
195			((char *)niov[ 0 ].iov_base)[ 3 ] = 0;
196		atpb.atp_sresiov = niov;
197		atpb.atp_sresiovcnt = 1;
198		if ( atp_sresp( atp, &atpb ) < 0 ) {
199		    LOG(log_error, logtype_papd, "atp_sresp: %s", strerror(errno) );
200		    exit( 1 );
201		}
202		return( 0 );
203		break;
204
205	    case PAP_TICKLE :
206		break;
207	    default :
208		LOG(log_error, logtype_papd, "Bad PAP request!" );
209	    }
210
211	    break;
212
213	case ATP_TRESP :
214	    atpb.atp_saddr = &ssat;
215	    for ( i = 0; i < oquantum; i++ ) {
216		niov[ i ].iov_len = PAP_MAXDATA + 4;
217	    }
218	    atpb.atp_rresiov = niov;
219	    atpb.atp_rresiovcnt = oquantum;
220	    if ( atp_rresp( atp, &atpb ) < 0 ) {
221		LOG(log_error, logtype_papd, "atp_rresp: %s", strerror(errno) );
222		return( -1 );
223	    }
224
225	    /* sanity */
226	    if ( ((unsigned char *)niov[ 0 ].iov_base)[ 0 ] != connid ||
227		    ((char *)niov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
228		LOG(log_error, logtype_papd, "Bad data response!" );
229		continue;
230	    }
231
232	    for ( i = 0; i < atpb.atp_rresiovcnt; i++ ) {
233		append( &infile,
234			(char *)niov[ i ].iov_base + 4, niov[ i ].iov_len - 4 );
235		if (( infile.pf_state & PF_EOF ) == 0 &&
236			((char *)niov[ 0 ].iov_base)[ 2 ] ) {
237		    infile.pf_state |= PF_EOF;
238		}
239	    }
240
241	    /* move data */
242	    if ( ps( &infile, &outfile, sat ) < 0 ) {
243		LOG(log_error, logtype_papd, "parse: bad return" );
244		return( -1 );	/* really?  close? */
245	    }
246
247	    /*
248	     * Ask for more data.
249	     */
250	    cbuf[ 0 ] = connid;
251	    cbuf[ 1 ] = PAP_READ;
252	    if ( ++seq == 0 ) seq = 1;
253	    netseq = htons( seq );
254	    memcpy( &cbuf[ 2 ], &netseq, sizeof( netseq ));
255	    atpb.atp_saddr = sat;
256	    atpb.atp_sreqdata = cbuf;
257	    atpb.atp_sreqdlen = 4;		/* bytes in SendData request */
258	    atpb.atp_sreqto = 5;		/* retry timer */
259	    atpb.atp_sreqtries = -1;		/* infinite retries */
260	    if ( atp_sreq( atp, &atpb, oquantum, ATP_XO )) {
261		LOG(log_error, logtype_papd, "atp_sreq: %s", strerror(errno) );
262		return( -1 );
263	    }
264	    break;
265
266	case 0:
267	    break;
268
269	default :
270	    LOG(log_error, logtype_papd, "atp_rsel: %s", strerror(errno) );
271	    return( -1 );
272	}
273
274	/* send any data that we have */
275	if ( readpending &&
276		( outfile.pf_datalen || ( outfile.pf_state & PF_EOF ))) {
277	    for ( i = 0; i < quantum; i++ ) {
278		((char *)niov[ i ].iov_base)[ 0 ] = connid;
279		((char *)niov[ i ].iov_base)[ 1 ] = PAP_DATA;
280		((char *)niov[ i ].iov_base)[ 2 ] =
281			((char *)niov[ i ].iov_base)[ 3 ] = 0;
282
283		if ( outfile.pf_datalen > PAP_MAXDATA  ) {
284		    cc = PAP_MAXDATA;
285		} else {
286		    cc = outfile.pf_datalen;
287		    if ( outfile.pf_state & PF_EOF ) {
288			((char *)niov[ 0 ].iov_base)[ 2 ] = 1;	/* eof */
289			outfile.pf_state = PF_BOT;
290			infile.pf_state = PF_BOT;
291		    }
292		}
293
294		niov[ i ].iov_len = 4 + cc;
295		memcpy( (char *)niov[ i ].iov_base + 4, outfile.pf_data, cc );
296		CONSUME( &outfile, cc );
297		if ( outfile.pf_datalen == 0 ) {
298		    i++;
299		    break;
300		}
301	    }
302	    ssat.sat_port = readport;
303	    atpb.atp_saddr = &ssat;
304	    atpb.atp_sresiov = niov;
305	    atpb.atp_sresiovcnt = i;	/* reported by stevebn@pc1.eos.co.uk */
306	    if ( atp_sresp( atp, &atpb ) < 0 ) {
307		LOG(log_error, logtype_papd, "atp_sresp: %s", strerror(errno) );
308		return( -1 );
309	    }
310	    readpending = 0;
311	}
312    }
313}
314