This repository has been archived by the owner on Sep 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwire.c
275 lines (210 loc) · 5.88 KB
/
wire.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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <arpa/inet.h>
#include "wire.h"
#define RXBUFLEN 500
#define TXBUFLEN 500
#define PACKETLEN 500
static int rxbuflen = RXBUFLEN; /* total size of the buffers */
static int txbuflen = TXBUFLEN;
static int tdbuflen = PACKETLEN;
static int rdbuflen = PACKETLEN;
/* This is twice the number of buffers actually needed, but having it all
available makes opt_H and opt_O handling much easier. */
static char rxbuf[RXBUFLEN]; /* [rt]xbuf are raw wire bytes, */
static char txbuf[TXBUFLEN]; /* with 7E delimiters and escaping */
static char rdbuf[PACKETLEN]; /* [rt]dbuf are for decoded data. */
static char tdbuf[PACKETLEN];
static int rxlen = 0; /* used space */
static int rxptr = 0; /* end of a complete packet, if any */
static int txlen = 0; /* length of stuff to send */
static int rdlen = 0;
static int tdlen = 0;
/* External interface. Due to the scope of the tool, there's always
a single connection and a single packet being processed. */
int hipfd = -1; /* opened modem tty */
struct hip* hip = NULL; /* current HIP packet being sent/recvd */
struct cns* cns = NULL; /* same, CnS packet */
/* It's very convenient to tap data flows here in wire.c instead of trying
to fetch the data from some other point. It is also makes sense to keep
dump*() in show.c where all generic output routines are.
To avoid exporting [rt][dx]buf, dump*() routines get them as arguments. */
extern int opt_H;
extern int opt_C;
extern int opt_O;
extern void dumphip(int len, char* buf);
extern void dumpcns(int len, char* buf);
/* Data to send is in tdbuf. Encode it for transmission, filling txbuf. */
static int encpacket(void)
{
if(tdbuflen < tdlen + 2)
return -1;
char *p, *q = txbuf;
char* txend = txbuf + txbuflen;
*(q++) = 0x7E;
for(p = tdbuf; p < tdbuf + tdlen && q < txend - 1; p++)
if(*p == 0x7E || *p == 0x7D) {
*(q++) = 0x7D;
*(q++) = (*p ^ 0x20);
} else {
*(q++) = *p;
}
if(q >= txend)
return -1;
*(q++) = 0x7E;
txlen = q - txbuf;
return 0;
}
/* There's a HIP packet in rxbuf, from 0 to rxptr.
Decode it into rdbuf, setting rdlen to the decoded length. */
static int decpacket(void)
{
char *p, *q;
char* rxend = rxbuf + rxptr;
char* rdend = rdbuf + rdbuflen;
/* rxptr points at the terminating 7E,
so it's p = rxbuf + 1 but p < rxend */
for(p = rxbuf + 1, q = rdbuf; p < rxend && q < rdend; p++)
if(*p == 0x7D)
*(q++) = *(++p) ^ 0x20;
else
*(q++) = *p;
if(p < rxend)
return -1;
rdlen = q - rdbuf;
return 0;
}
/* Unencoded data to send is in txbuf, its length is txlen.
Encode the packet and send it. */
int encodesend(void)
{
if(opt_H && opt_O)
dumphip(tdlen, tdbuf);
/* This will alter hiplen */
if(encpacket())
return -1;
int sent = write(hipfd, txbuf, txlen);
if(sent < 0)
err(6, "send failed");
if(sent != txlen)
err(6, "send: only %i bytes out of %i", sent, txlen);
return 0;
}
/* Skip garbage (that's anything before 7E) at the start of rxbuf */
static void pullrx(int d)
{
memmove(rxbuf, rxbuf + d, rxlen - d);
rxlen -= d;
}
/* Receive some bytes, and do basic HIP packet checks.
Return packet length when there's a full HIP packet at the start of rxbuf,
or 0 if there' none. */
static int packetend(void)
{
char* p;
int ltt;
again: if(!(p = memchr(rxbuf, 0x7E, rxlen)))
return 0;
/* skip leading garbage */
if((ltt = p - rxbuf) > 0)
pullrx(ltt);
/* do we have terminal byte? */
if(!(p = memchr(rxbuf + 1, 0x7E, rxlen - 1)))
return 0;
/* is there anything resembling a packet? */
if(p < rxbuf + sizeof(struct hip)) {
pullrx(p - rxbuf);
goto again;
}
return (p - rxbuf);
}
int sendhip(int mid, int par, int len, char* data)
{
hip = (struct hip*) tdbuf;
if(len > tdbuflen - sizeof(struct hip))
return -1;
hip->mid = mid;
hip->par = par;
hip->len = htons(len);
memcpy(hip->payload, data, len);
tdlen = sizeof(struct hip) + len;
return encodesend();
}
int sendcns(int oid, int op, int len, const char* data)
{
hip = (struct hip*) tdbuf;
cns = (struct cns*) hip->payload;
hip->mid = HIP_CNS_H2M;
hip->par = 0x00;
hip->len = htons(sizeof(struct cns) + len);
if(len > tdbuflen - sizeof(struct cns) - sizeof(struct hip))
return -1;
cns->oid = htons(oid);
cns->op = op;
cns->app = 0x00000000;
cns->len = htons(len);
if(len && data)
memcpy(cns->payload, data, len);
tdlen = sizeof(struct hip) + sizeof(struct cns) + len;
if(opt_C && opt_O)
dumpcns(ntohs(hip->len), hip->payload);
return encodesend();
}
/* Get next HIP packet. Generally means reading from the TTY,
but may also return immediately if there's one more in rxbuf. */
int recvhip(void)
{
int rd;
/* Remove old (already decoded) packet if there's one */
hip = NULL;
if(rxptr) {
pullrx(rxptr);
rxptr = 0;
}
/* Blocking complete read for now */
while(!(rxptr = packetend()))
if((rd = read(hipfd, rxbuf + rxlen, rxbuflen - rxlen)) <= 0)
return -1;
else
rxlen += rd;
if(decpacket())
return -1;
if(opt_H)
dumphip(rdlen, rdbuf);
hip = (struct hip*) rdbuf;
int explen = ntohs(hip->len);
int actlen = rdlen - sizeof(struct hip);
if(explen > actlen) {
warnx("HIP packet expected %i got %i bytes", explen, actlen);
hip->len = htons(actlen);
}
return 0;
}
/* Receive a HIP, and set *cns if it happens to be a CnS packet.
Not every HIP packet has CnS payload, though it does not look
like the device ever sends non-CnS HIPs on its own. */
int recvcns(void)
{
cns = NULL;
int r = recvhip();
if(r < 0)
return -1;
if(r > 0)
return 1;
if(hip->mid != HIP_CNS_M2H)
return 1;
cns = (struct cns*) hip->payload;
int explen = ntohs(cns->len);
int actlen = ntohs(hip->len);
/* ^ assumed to be reasonable, see recvhip() */
if(explen > actlen) {
warnx("CNS packet expected %i got %i bytes", explen, actlen);
cns->len = htons(actlen);
}
if(opt_C)
dumpcns(ntohs(hip->len), hip->payload);
return 0;
}