forked from gyepisam/spiped
-
Notifications
You must be signed in to change notification settings - Fork 0
/
proto_pipe.c
202 lines (166 loc) · 4.3 KB
/
proto_pipe.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
#include <sys/types.h>
#include <sys/socket.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "network.h"
#include "pcrypt.h"
#include "proto_pipe.h"
struct pipe_cookie {
int (* callback)(void *);
void * cookie;
int * status;
int s_in;
int s_out;
int decr;
struct proto_keys * k;
uint8_t dbuf[PCRYPT_MAXDSZ];
uint8_t ebuf[PCRYPT_ESZ];
void * read_cookie;
void * write_cookie;
ssize_t wlen;
};
static int callback_pipe_read(void *, ssize_t);
static int callback_pipe_write(void *, ssize_t);
/**
* proto_pipe(s_in, s_out, decr, k, status, callback, cookie):
* Read bytes from ${s_in} and write them to ${s_out}. If ${decr} is non-zero
* then use ${k} to decrypt the bytes; otherwise use ${k} to encrypt them.
* If EOF is read, set ${status} to 0, and if an error is encountered set
* ${status} to -1; in either case, invoke ${callback}(${cookie}). Return a
* cookie which can be passed to proto_pipe_cancel.
*/
void *
proto_pipe(int s_in, int s_out, int decr, struct proto_keys * k,
int * status, int (* callback)(void *), void * cookie)
{
struct pipe_cookie * P;
/* Bake a cookie. */
if ((P = malloc(sizeof(struct pipe_cookie))) == NULL)
goto err0;
P->callback = callback;
P->cookie = cookie;
P->status = status;
P->s_in = s_in;
P->s_out = s_out;
P->decr = decr;
P->k = k;
P->read_cookie = NULL;
P->write_cookie = NULL;
/* Start reading. */
if (P->decr) {
if ((P->read_cookie = network_read(P->s_in, P->ebuf,
PCRYPT_ESZ, PCRYPT_ESZ, callback_pipe_read, P)) == NULL)
goto err1;
} else {
if ((P->read_cookie = network_read(P->s_in, P->dbuf,
PCRYPT_MAXDSZ, 1, callback_pipe_read, P)) == NULL)
goto err1;
}
/* Success! */
return (P);
err1:
free(P);
err0:
/* Failure! */
return (NULL);
}
/* Some data has been read. */
static int
callback_pipe_read(void * cookie, ssize_t len)
{
struct pipe_cookie * P = cookie;
/* This read is no longer in progress. */
P->read_cookie = NULL;
/* Did we read EOF? */
if (len == 0)
goto eof;
/* Did the read fail? */
if (len == -1)
goto fail;
/* Did a packet read end prematurely? */
if ((P->decr) && (len < PCRYPT_ESZ))
goto fail;
/* Encrypt or decrypt the data. */
if (P->decr) {
if ((P->wlen = pcrypt_dec(P->ebuf, P->dbuf, P->k)) == -1)
goto fail;
} else {
pcrypt_enc(P->dbuf, len, P->ebuf, P->k);
P->wlen = PCRYPT_ESZ;
}
/* Write the encrypted or decrypted data. */
if (P->decr) {
if ((P->write_cookie = network_write(P->s_out, P->dbuf,
P->wlen, P->wlen, callback_pipe_write, P)) == NULL)
goto err0;
} else {
if ((P->write_cookie = network_write(P->s_out, P->ebuf,
P->wlen, P->wlen, callback_pipe_write, P)) == NULL)
goto err0;
}
/* Success! */
return (0);
fail:
/* Record that this connection is broken. */
*(P->status) = 0;
/* Inform the upstream that our status has changed. */
return ((P->callback)(P->cookie));
eof:
/* We aren't going to write any more. */
shutdown(P->s_out, SHUT_WR);
/* Record that we have reached EOF. */
*(P->status) = 0;
/* Inform the upstream that our status has changed. */
return ((P->callback)(P->cookie));
err0:
/* Failure! */
return (-1);
}
static int
callback_pipe_write(void * cookie, ssize_t len)
{
struct pipe_cookie * P = cookie;
/* This write is no longer in progress. */
P->write_cookie = NULL;
/* Did we fail to write everything? */
if (len < P->wlen)
goto fail;
/* Launch another read. */
if (P->decr) {
if ((P->read_cookie = network_read(P->s_in, P->ebuf,
PCRYPT_ESZ, PCRYPT_ESZ, callback_pipe_read, P)) == NULL)
goto err0;
} else {
if ((P->read_cookie = network_read(P->s_in, P->dbuf,
PCRYPT_MAXDSZ, 1, callback_pipe_read, P)) == NULL)
goto err0;
}
/* Success! */
return (0);
fail:
/* Record that this connection is broken. */
*(P->status) = 0;
/* Inform the upstream that our status has changed. */
return ((P->callback)(P->cookie));
err0:
/* Failure! */
return (-1);
}
/**
* proto_pipe_cancel(cookie):
* Shut down the pipe created by proto_pipe for which ${cookie} was returned.
*/
void
proto_pipe_cancel(void * cookie)
{
struct pipe_cookie * P = cookie;
/* If a read or write is in progress, cancel it. */
if (P->read_cookie)
network_read_cancel(P->read_cookie);
if (P->write_cookie)
network_write_cancel(P->write_cookie);
/* Free the cookie. */
free(P);
}