1 /* Copyright StrongLoop, Inc. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22 #include "s5.h"
23 #include <errno.h>
24 #include <stdint.h>
25 #include <stdlib.h> /* abort() */
26 #include <string.h> /* memset() */
27
28 enum {
29 s5_version,
30 s5_nmethods,
31 s5_methods,
32 s5_auth_pw_version,
33 s5_auth_pw_userlen,
34 s5_auth_pw_username,
35 s5_auth_pw_passlen,
36 s5_auth_pw_password,
37 s5_req_version,
38 s5_req_cmd,
39 s5_req_reserved,
40 s5_req_atyp,
41 s5_req_atyp_host,
42 s5_req_daddr,
43 s5_req_dport0,
44 s5_req_dport1,
45 s5_dead
46 };
47
s5_init(s5_ctx * cx)48 void s5_init(s5_ctx *cx) {
49 memset(cx, 0, sizeof(*cx));
50 cx->state = s5_version;
51 }
52
s5_parse(s5_ctx * cx,uint8_t ** data,size_t * size)53 s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size) {
54 s5_err err;
55 uint8_t *p;
56 uint8_t c;
57 size_t i;
58 size_t n;
59
60 p = *data;
61 n = *size;
62 i = 0;
63
64 while (i < n) {
65 c = p[i];
66 i += 1;
67 switch (cx->state) {
68 case s5_version:
69 if (c != 5) {
70 err = s5_bad_version;
71 goto out;
72 }
73 cx->state = s5_nmethods;
74 break;
75
76 case s5_nmethods:
77 cx->arg0 = 0;
78 cx->arg1 = c; /* Number of bytes to read. */
79 cx->state = s5_methods;
80 break;
81
82 case s5_methods:
83 if (cx->arg0 < cx->arg1) {
84 switch (c) {
85 case 0:
86 cx->methods |= S5_AUTH_NONE;
87 break;
88 case 1:
89 cx->methods |= S5_AUTH_GSSAPI;
90 break;
91 case 2:
92 cx->methods |= S5_AUTH_PASSWD;
93 break;
94 /* Ignore everything we don't understand. */
95 }
96 cx->arg0 += 1;
97 }
98 if (cx->arg0 == cx->arg1) {
99 err = s5_auth_select;
100 goto out;
101 }
102 break;
103
104 case s5_auth_pw_version:
105 if (c != 1) {
106 err = s5_bad_version;
107 goto out;
108 }
109 cx->state = s5_auth_pw_userlen;
110 break;
111
112 case s5_auth_pw_userlen:
113 cx->arg0 = 0;
114 cx->userlen = c;
115 cx->state = s5_auth_pw_username;
116 break;
117
118 case s5_auth_pw_username:
119 if (cx->arg0 < cx->userlen) {
120 cx->username[cx->arg0] = c;
121 cx->arg0 += 1;
122 }
123 if (cx->arg0 == cx->userlen) {
124 cx->username[cx->userlen] = '\0';
125 cx->state = s5_auth_pw_passlen;
126 }
127 break;
128
129 case s5_auth_pw_passlen:
130 cx->arg0 = 0;
131 cx->passlen = c;
132 cx->state = s5_auth_pw_password;
133 break;
134
135 case s5_auth_pw_password:
136 if (cx->arg0 < cx->passlen) {
137 cx->password[cx->arg0] = c;
138 cx->arg0 += 1;
139 }
140 if (cx->arg0 == cx->passlen) {
141 cx->password[cx->passlen] = '\0';
142 cx->state = s5_req_version;
143 err = s5_auth_verify;
144 goto out;
145 }
146 break;
147
148 case s5_req_version:
149 if (c != 5) {
150 err = s5_bad_version;
151 goto out;
152 }
153 cx->state = s5_req_cmd;
154 break;
155
156 case s5_req_cmd:
157 switch (c) {
158 case 1: /* TCP connect */
159 cx->cmd = s5_cmd_tcp_connect;
160 break;
161 case 3: /* UDP associate */
162 cx->cmd = s5_cmd_udp_assoc;
163 break;
164 default:
165 err = s5_bad_cmd;
166 goto out;
167 }
168 cx->state = s5_req_reserved;
169 break;
170
171 case s5_req_reserved:
172 cx->state = s5_req_atyp;
173 break;
174
175 case s5_req_atyp:
176 cx->arg0 = 0;
177 switch (c) {
178 case 1: /* IPv4, four octets. */
179 cx->state = s5_req_daddr;
180 cx->atyp = s5_atyp_ipv4;
181 cx->arg1 = 4;
182 break;
183 case 3: /* Hostname. First byte is length. */
184 cx->state = s5_req_atyp_host;
185 cx->atyp = s5_atyp_host;
186 cx->arg1 = 0;
187 break;
188 case 4: /* IPv6, sixteen octets. */
189 cx->state = s5_req_daddr;
190 cx->atyp = s5_atyp_ipv6;
191 cx->arg1 = 16;
192 break;
193 default:
194 err = s5_bad_atyp;
195 goto out;
196 }
197 break;
198
199 case s5_req_atyp_host:
200 cx->arg1 = c;
201 cx->state = s5_req_daddr;
202 break;
203
204 case s5_req_daddr:
205 if (cx->arg0 < cx->arg1) {
206 cx->daddr[cx->arg0] = c;
207 cx->arg0 += 1;
208 }
209 if (cx->arg0 == cx->arg1) {
210 cx->daddr[cx->arg1] = '\0';
211 cx->state = s5_req_dport0;
212 }
213 break;
214
215 case s5_req_dport0:
216 cx->dport = c << 8;
217 cx->state = s5_req_dport1;
218 break;
219
220 case s5_req_dport1:
221 cx->dport |= c;
222 cx->state = s5_dead;
223 err = s5_exec_cmd;
224 goto out;
225
226 case s5_dead:
227 break;
228
229 default:
230 abort();
231 }
232 }
233 err = s5_ok;
234
235 out:
236 *data = p + i;
237 *size = n - i;
238 return err;
239 }
240
s5_auth_methods(const s5_ctx * cx)241 unsigned int s5_auth_methods(const s5_ctx *cx) {
242 return cx->methods;
243 }
244
s5_select_auth(s5_ctx * cx,s5_auth_method method)245 int s5_select_auth(s5_ctx *cx, s5_auth_method method) {
246 int err;
247
248 err = 0;
249 switch (method) {
250 case S5_AUTH_NONE:
251 cx->state = s5_req_version;
252 break;
253 case S5_AUTH_PASSWD:
254 cx->state = s5_auth_pw_version;
255 break;
256 default:
257 err = -EINVAL;
258 }
259
260 return err;
261 }
262
s5_strerror(s5_err err)263 const char *s5_strerror(s5_err err) {
264 #define S5_ERR_GEN(_, name, errmsg) case s5_ ## name: return errmsg;
265 switch (err) {
266 S5_ERR_MAP(S5_ERR_GEN)
267 default: ; /* Silence s5_max_errors -Wswitch warning. */
268 }
269 #undef S5_ERR_GEN
270 return "Unknown error.";
271 }
272