-
Notifications
You must be signed in to change notification settings - Fork 1
/
zfin.c
261 lines (212 loc) · 6.57 KB
/
zfin.c
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/* zfin.c
* Scott Bronson
* 13 June 2005
*
* Implements a fifo proc that scans for the ZFIN end packet.
*/
/** @file zfin.c
*
* Scans for the ZFIN packet and tries to assure an orderly shutdown.
*
* Terminating the connection without sending garbage onto the user's
* terminal is made somewhat complex due to signfiicant zmodem weirdness.
* Here's how we do it now:
*
* 1) Sender sends ZFIN
* Child receives the ZFIN
* 2) Child sends ZFIN in response
* Sender receives it
* Sender might send "OO" (over and out) or not. Stupid protocol.
* We shut the child down immediately after it sends the zfin
* This prevents further packets from hitting the sender
* and being interpreted as shell commands.
*
* So, when we recognize a ZFIN from the child:
* We turn off that fifo. No more data will be read from the child.
* zfin_scan -> zfin_term -> zfin_drop
* We kill the child. We know the sender has already sent a ZFIN.
*
* When we recognize a ZFIN from the master:
* We supporess a further optional "OO"
* We ensure that all further data gets written to stdout.
* So, what we do, we store all data read from the master AFTER the ZFIN/OO
* Then, when the pipes are restored, the data is written back into the pipe
* So the master actually cycles through 3 procs while scanning:
* zfin_scan -> zfin_nooo -> zfin_save
* Then, in the destructor, we copy the saved data back into the output pipe.
*/
#include "log.h"
#include "fifo.h"
#include "io/io.h"
#include "pipe.h"
#include "task.h"
#include "zfin.h"
#include "util.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
zfinscanstate* zfin_create(master_pipe *mp,
void (*proc)(struct fifo *f, const char *buf, int size, int fd))
{
zfinscanstate *state;
state = malloc(sizeof(zfinscanstate));
if(state == NULL) {
perror("allocating zfinscanstate");
bail(56);
}
memset(state, 0, sizeof(zfinscanstate));
state->master = mp;
state->found = proc;
return state;
}
void zfin_destroy(zfinscanstate *state)
{
if(state->savebuf) {
free(state->savebuf);
}
free(state);
}
#if 0
// This wrapper verifies that zfin_scan doesn't modify its data in any way.
void zfin_scan(struct fifo *f, const char *buf, int size, int fd)
{
log_warn("enter: size=%d refcon=%08lX", size, (long)f->refcon);
int ofe = f->end;
orig_zfin_scan(f, buf, size, fd);
int nfe = f->end;
if(nfe >= ofe) {
if(nfe - ofe != size) {
log_warn("sizes differ!");
} else if(memcmp(f->buf+ofe, buf, size) != 0) {
log_warn("contents differ!");
}
} else {
// skip this for now
log_warn("wrap!");
}
}
#define zfin_scan orig_zfin_scan
#else
void zfin_scan(struct fifo *f, const char *buf, int size, int fd)
{
// technically this string should begin with "**". however there's
// a boyer-mooreesque start problem: if the previous packet ends with
// "*", and the next packet is a ZFIN, this match algorightm won't
// find it. It finds "**" (one star from prevous packet, one star
// from the next packet), fails to match \030 against the second star
// in the second packet, and starts over. But now we only have one
// star! The proper way is to implement a complex restart procedure.
// Much easier, though, to just search for the single star. Since it's
// impossible for this sequence to be anywhere in a zmodem transfer
// OTHER than a ZFIN packet, it's an adequate workaround.
static const char zfin[] = "*\030B0800000000022d\015\212";
zfinscanstate *state = (zfinscanstate*)f->refcon;
const char *ref = state->ref;
const char *cp, *ce;
if(size <= 0) {
return;
}
for(;;) {
if(ref) {
cp = buf;
// log_dbg("zfin %d resuming string match at %d", fd, ref-zfin);
} else {
// log_dbg("zfin on %d: searching for '*' in %d bytes", fd, size);
cp = memchr(buf, '*', size);
if(!cp) {
// log_dbg("zfin on %d: '*' not found, returning entire buffer", fd);
// couldn't find the start char in the entire buffer.
fifo_unsafe_append(f, buf, size);
return;
}
// found the start char. flush all the data up to the start char.
fifo_unsafe_append(f, buf, cp-buf);
size -= cp-buf;
buf = cp;
// and set up the state var
ref = zfin;
}
ce = buf + size;
while(cp < ce && *cp == *ref && ref < zfin+sizeof(zfin)-1) {
cp += 1;
ref += 1;
}
// append the data to the fifo no matter what.
fifo_unsafe_append(f, buf, cp-buf);
size -= cp-buf;
buf = cp;
if(*ref == 0) {
f->proc = state->found;
(*f->proc)(f, buf, size, fd);
return;
} else if(cp >= ce) {
// out of data, so we'll return and get called with more
state->ref = ref;
return;
} else if(*cp != *ref) {
// couldn't match. append what we have so far.
ref = NULL;
} else {
assert(!"This should be unpossible!");
}
}
}
#endif
/** No OO: drops an optional OO, then passes the rest to zfinsave */
// TODO: this doesn't appear to work 100%
void zfin_nooo(struct fifo *f, const char *buf, int size, int fd)
{
zfinscanstate *state = (zfinscanstate*)f->refcon;
while(size > 0) {
// move to the next routine if we've suppressed 2 Os
// or there are no more Os to be found.
if(*buf != 'O' || state->oocount >= 2) {
f->proc = zfin_save;
(*f->proc)(f, buf, size, fd);
return;
}
// log_info("Dropped an O after ZFIN on %d", fd);
buf += 1;
size -= 1;
state->oocount += 1;
}
}
/** Saves all text in a dynamic buffer. When the destructor is
* called, the saved text will be inserted into the pipe (now
* reconnected to the terminal instead of to the receive process).
*/
void zfin_save(struct fifo *f, const char *buf, int size, int fd)
{
zfinscanstate *state = (zfinscanstate*)f->refcon;
if(size <= 0) {
return;
}
if(size + state->savecnt > state->savemax) {
state->savemax += size + 512;
log_info("Reallocing save buffer to %d bytes.", (int)state->savemax);
state->savebuf = realloc(state->savebuf, state->savemax);
if(state->savebuf == NULL) {
perror("reallocing savebuf");
bail(57);
}
}
log_info("SAVING %d bytes from %d: %s", size, fd, sanitize(buf, size));
memcpy(state->savebuf + state->savecnt, buf, size);
state->savecnt += size;
}
void zfin_term(struct fifo *f, const char *buf, int size, int fd)
{
zfinscanstate *state = (zfinscanstate*)f->refcon;
task_terminate(state->master);
f->proc = zfin_drop;
(*f->proc)(f, buf, size, fd);
}
void zfin_drop(struct fifo *f, const char *buf, int size, int fd)
{
if(size <= 0) {
return;
}
log_info("IGNORE from %d: \"%s\"", fd, sanitize(buf, size));
}