File: xmodem.c

package info (click to toggle)
mgetty 1.2.1-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,880 kB
  • sloc: ansic: 42,728; sh: 6,487; perl: 6,262; makefile: 1,457; tcl: 756; lisp: 283
file content (240 lines) | stat: -rw-r--r-- 5,759 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#ident "$Id: xmodem.c,v 1.3 1999/04/11 21:11:36 gert Exp $"

/* xmodem.c
 *
 * XModem-primitives for uploading and downloading from an already 
 * open file descriptor (no terminal handling etc)
 *
 * warning: these functions use alarm() and mess up SIGALRM handlers
 *
 * $Log: xmodem.c,v $
 * Revision 1.3  1999/04/11 21:11:36  gert
 * cleanup
 *
 * Revision 1.2  1999/04/05 20:43:15  gert
 * - add ELSA-EOT (NAKing EOT doesn't work, recognize 0d/0a/OK/0d/0a
 * - add "all-in-one" download function
 * - fix timeout bug
 *
 * Revision 1.1  1999/03/31 21:03:22  gert
 * XModem primitives - first cut, downloading works
 *
 */

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#ifndef ENOENT
# include <errno.h>
#endif

#include "mgetty.h"

/* defines for the XModem protocol */
#define ACK	0x06			/* Acknowledge */
#define NAK	0x15			/* Not Acknowledge */
#define CAN	0x18			/* CANcel transmission (ctrl-X) */
#define SOH	0x01			/* start of block, 128byte */
#define STX	0x02			/* start of block, 1024byte */
#define EOT	0x04			/* end of transmission */

static char c_ack = ACK, c_nak = NAK;	/* to be used for write() */

static int x_errcnt=0;			/* error counter */
static int x_eotcnt=0;			/* EOT counter */
       int xmodem_blk=0;		/* current block number */

/* message function is specified by a pointer to a (char *, ...) function */
typedef void (*msg_func_t)(char *,...);

/* semi-globals */
static int x_fd;			/* file descriptor to "modem" */
static msg_func_t x_msg_func;		/* message function for logging */

/* prototypes */
static int x_nak(void);
int xmodem_rcv_block _PROTO((unsigned char *buf));

/* helper for x_timeout handling */
static int x_timeout=0;
static void x_sigalarm(int s)
{ 
    signal(SIGALRM, x_sigalarm); 
    x_timeout=1;
}

/* initialize XModem receiver
 */
int xmodem_rcv_init _P3((fd, msg_func, buf),
			int fd, msg_func_t msg_func, char *buf)
{
    x_fd = fd;
    x_msg_func = msg_func;
    signal(SIGALRM, x_sigalarm);

    xmodem_blk = 1;
    x_errcnt = x_eotcnt = 0;

    x_nak();
    return xmodem_rcv_block(buf);
}

/* get next block
 */
int xmodem_rcv_block _P1((buf), unsigned char *buf)
{
unsigned char ch;
int got_can=0, ok_seq_cnt=0;
int len, i, chks;
static char ok_seq[] = { 0x0d, 0x0a, 'O', 'K', 0x0d, 0x0a };

again:
    alarm(10); 

    lprintf( L_NOISE, "xmodem_rcv_block, got:" );

    do
    {
	if ( read( x_fd, &ch, 1 ) < 1 ) { goto send_nak; }
	lputc( L_NOISE, ch );

	/* if we get two CANs in sequence, give up
	 */
	if ( ch == CAN ) 
	    { lprintf( L_NOISE, "<CAN>" );
	      if ( ++got_can > 1 ) { x_errcnt=10; return -1;}}
	else 
	    { got_can=0; }

	/* the XModem protocol description suggests that the first EOT
	 * should be NAKed, and the second one should be accepted - 
	 * problem with that is, the ELSA doesn't send a second EOT,
	 * so we accept \r\nOK\r\n as "second EOT"
	 */
	if ( ch == EOT ) 
	{ 
	    x_eotcnt++;
	    if ( x_eotcnt < 2 )		/* false alarm? NAK! */
	    	goto send_nak;
	    return 0;			/* EOF */
	}

	if ( ch == ok_seq[ok_seq_cnt] )
	{
	    ok_seq_cnt++;
	    lputs( L_JUNK, "<s>" );
	    if ( ok_seq_cnt >= 6 )
	    	{ lprintf( L_NOISE, "ELSA-EOT" ); return 0; }
	}
	else ok_seq_cnt= ( ch == ok_seq[0] );
    }
    while( ch != SOH && ch != STX );

    len = ( ch == SOH )? 128: 1024;
    lprintf( L_NOISE, "got SOH/STX, block size=%d", len );

    if ( read( x_fd, &ch, 1 ) < 1 || ch != (xmodem_blk & 0xff) ) 
	{ fprintf( stderr, "Block # %d doesn't match %d\n", ch, xmodem_blk); 
	  goto send_nak; }
    if ( read( x_fd, &ch, 1 ) < 1 || ch != 255-(xmodem_blk & 0xff) ) 
	{ fprintf( stderr, "!Block # %d doesn't match %d\n", ch, xmodem_blk); 
	  goto send_nak; }
    
    lprintf( L_NOISE, "expected seq.# %d ok, read block", xmodem_blk );
    chks=0;

    for( i=0; i<len; i++ )
    {
	if ( read( x_fd, &buf[i], 1 ) < 1 ) { goto send_nak; }
	chks += buf[i];
    }

    if ( read( x_fd, &ch, 1 ) < 1 || ch != (chks &0xff)) 
    { lprintf( L_WARN, "Chksum %02x doesn't match %02x\n", ch, (chks & 0xff)); 
      goto send_nak; }

    alarm(0);
    
    lprintf( L_NOISE, "Block %d len %d ok, send ACK\n", xmodem_blk, len );
    write( x_fd, &c_ack, 1 );
    xmodem_blk++;
    return len;

send_nak:
    x_nak();
    x_errcnt++;
    if ( x_errcnt<10 ) goto again;

    alarm(0);
    lprintf( L_MESG, "too many NAKs, giving up" );
    return -1;
}

static int x_nak(void)
{
    char ch;

    lprintf( L_JUNK, "x_nak: drain queue, send NAK, got:" );

    /* drain input queue - make sure we're not in the middle of a block
     */
    x_timeout=0;
    while(!x_timeout)
    { 
	alarm(1);
	if ( read( x_fd, &ch, 1 ) != 1 && errno != EINTR ) 
		{ alarm(0); return -1; }
	lputc( L_JUNK, ch );
    }

    /* send NAK */
    return write( x_fd, &c_nak, 1 );
}

int xmodem_rcv_file _P3(( modem_fd, out_fd, msg_func),
			 int modem_fd, int out_fd, msg_func_t msg_func )
{
int s;
char buf[1030];

    s = xmodem_rcv_init( modem_fd, msg_func, buf );

    if ( s <= 0 ) 
    {
        lprintf( L_ERROR, "XModem startup failed" );
        fprintf( stderr, "XModem startup failed\n" );
	close(out_fd);
	return -1;
    }

    do
    {
    	if ( write( out_fd, buf, s ) != s ) 
	{
	    lprintf( L_ERROR, "can't write %d bytes to file: %s",
	    		s, strerror(errno) );
	    fprintf( stderr, "can't write %d bytes to file: %s\n",
	    		s, strerror(errno) );
	    close(out_fd);
	    return -1;
	}
	printf( "block #%d\r", xmodem_blk ); fflush( stdout );

        s = xmodem_rcv_block( buf );
    }
    while( s > 0 );

    if ( s < 0 )
    {
    	lprintf( L_ERROR, "can't receive expected block" );
    	fprintf( stderr, "can't receive expected block\n" );
	return -1;
    }

    close(out_fd);

    return 0;
}