1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright (c) 1998-1999,2001 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <stdio.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <string.h>
33#include "snoop.h"
34
35static void put_method(char *cp, int method);
36static void put_socks5_addr(char *cp, const unsigned char *buf, int fraglen);
37static void put_socks4_res(char *cp, int code);
38static void put_socks5_res(char *cp, int code);
39
40int
41interpret_socks_call(flags, line, fraglen)
42	int flags;
43	char *line;
44	int fraglen;
45{
46	unsigned char	*buf = (unsigned char *)line;
47	char		*cp;
48	struct in_addr	ipaddr;
49	int		i, n;
50
51	if (flags & F_SUM) {
52	cp = get_sum_line();
53	if (fraglen >= 2) {
54		switch (buf[0]) {
55		case 4:		/* SOCKS4 */
56			n = buf[1];
57			switch (n) {
58			case 1:
59			case 2:
60				if (fraglen >= 8) {
61					(void) memcpy(&ipaddr, &buf[4],
62					    sizeof (ipaddr));
63					(void) sprintf(cp,
64					    "SOCKS4 %s %s:%u",
65					    addrtoname(AF_INET, &ipaddr),
66					    (n == 1)? "CONNECT": "BIND",
67					    (buf[2] << 8) | buf[3]);
68					cp += strlen(cp);
69					if (fraglen > 8) {
70						(void) sprintf(cp, " User=");
71						cp += strlen(cp);
72						for (i = 8;
73							i < 40 && i < fraglen;
74							++i) {
75							if (buf[i] == '\0')
76								break;
77							*cp++ = buf[i];
78						}
79						if (i == 40) {
80							*cp++ = '.';
81							*cp++ = '.';
82							*cp++ = '.';
83						}
84						*cp = '\0';
85					}
86				}
87				break;
88			default:
89				(void) sprintf(cp, "SOCKS4 OP=%u", n);
90			}
91			break;
92		case 5:		/* SOCKS5 */
93			n = buf[1];
94			if (2 + n == fraglen) {
95				(void) sprintf(cp,
96					"SOCKS5 CONTACT NMETHODS=%d:", n);
97				cp += strlen(cp);
98				for (i = 0; i < n && 2 + i < fraglen; ++i) {
99					put_method(cp, buf[2 + i]);
100					cp += strlen(cp);
101				}
102			} else if (fraglen >= 6 && buf[2] == 0) {
103				const char	*cmd;
104
105				if (n < 1 || n > 3) {
106					(void) sprintf(cp,
107						"SOCKS (send data): %s",
108						show_string(line, fraglen, 20));
109				} else {
110					switch (n) {
111					case 1:
112						cmd = "CONNECT";
113						break;
114					case 2:
115						cmd = "BIND";
116						break;
117					case 3:
118						cmd = "ASSOCIATE_UDP";
119						break;
120					}
121					(void) sprintf(cp, "SOCKS5 %s ", cmd);
122					cp += strlen(cp);
123					put_socks5_addr(cp, &buf[3],
124						fraglen - 3);
125				}
126			} else {
127				(void) sprintf(cp, "SOCKS (send data): %s",
128					show_string(line, fraglen, 20));
129			}
130			break;
131		default:
132			(void) sprintf(cp, "SOCKS (send data): %s",
133				show_string(line, fraglen, 20));
134		}
135	} else {
136		(void) sprintf(cp, "SOCKS (send data): %s",
137			show_string(line, fraglen, 20));
138	}
139
140	} /* if (flags & F_SUM) */
141
142	if (flags & F_DTAIL) {
143		show_header("SOCKS: ", "SOCKS Header", fraglen);
144		show_space();
145		cp = get_line(0, 0);
146		if (fraglen >= 2) {
147			switch (buf[0]) {
148			case 4:
149				(void) sprintf(cp, "Version = 4");
150				n = buf[1];
151				switch (n) {
152				case 1:
153				case 2:
154					(void) sprintf(get_line(0, 0),
155					    "Operation = %s",
156					    (n == 1)? "CONNECT": "BIND");
157					if (fraglen >= 8) {
158						(void) memcpy(&ipaddr, &buf[4],
159						    sizeof (ipaddr));
160						(void) sprintf(get_line(0, 0),
161						    "Destination = %s:%u",
162						    addrtoname(AF_INET,
163						    &ipaddr),
164						    (buf[2] << 8) | buf[3]);
165						if (fraglen > 8) {
166							cp = get_line(0, 0);
167							(void) sprintf(cp,
168							    "User = ");
169							cp += strlen(cp);
170							for (i = 8;
171								i < 40; ++i) {
172								if
173								(buf[i] == '\0')
174									break;
175								*cp++ = buf[i];
176							}
177							if (i == 40) {
178								*cp++ = '.';
179								*cp++ = '.';
180								*cp++ = '.';
181							}
182							*cp = '\0';
183						}
184					}
185					break;
186				default:
187					(void) sprintf(get_line(0, 0),
188					    "Operation = %u (unknown)", n);
189				}
190				break;
191			case 5:		/* SOCKS5 */
192				(void) sprintf(cp, "Version = 5");
193				n = buf[1];
194				if (2 + n == fraglen) {
195					(void) sprintf(get_line(0, 0),
196					    "Number of methods = %u", n);
197					for (i = 0;
198						i < n && 2 + i < fraglen; ++i) {
199						cp = get_line(0, 0);
200						(void) sprintf(cp,
201							"Method %3u =", i);
202						cp += strlen(cp);
203						put_method(cp, buf[2 + i]);
204					}
205				} else if (fraglen >= 6 && buf[2] == 0) {
206					const char	*cmd;
207					if (n < 1 || n > 3) {
208						(void) sprintf(cp,
209							"SOCKS (send data): %s",
210							show_string(line,
211							fraglen, 20));
212					} else {
213						switch (n) {
214						case 1:
215							cmd = "CONNECT";
216							break;
217						case 2:
218							cmd = "BIND";
219							break;
220						case 3:
221							cmd = "ASSOCIATE_UDP";
222							break;
223						}
224						(void) sprintf(get_line(0, 0),
225						    "Operation = %s ", cmd);
226						put_socks5_addr(get_line(0, 0),
227						    &buf[3], fraglen - 3);
228						break;
229					}
230				} else
231					(void) sprintf(cp,
232						" SOCKS (send data): %s",
233						show_string(line, fraglen,
234						20));
235				break;
236			default:
237				(void) sprintf(cp,
238					"SOCKS (send data): %s",
239					show_string(line, fraglen, 20));
240			}
241			show_space();
242		} else
243			(void) sprintf(cp,
244				"SOCKS (send data): %s",
245				show_string(line, fraglen, 20));
246	}
247
248out:
249	return (fraglen);
250}
251
252int
253interpret_socks_reply(flags, line, fraglen)
254	int flags;
255	char *line;
256	int fraglen;
257{
258	unsigned char	*buf = (unsigned char *)line;
259	char		*cp;
260	struct in_addr	ipaddr;
261
262	if (flags & F_SUM) {
263		cp = get_sum_line();
264		if (fraglen >= 2) {
265			switch (buf[0]) {
266			case 0:
267				(void) sprintf(cp, "SOCKS4 ");
268				cp += strlen(cp);
269				if (fraglen >= 8) {
270					(void) memcpy(&ipaddr, &buf[4],
271					    sizeof (ipaddr));
272					(void) sprintf(cp, "%s:%u ",
273					    addrtoname(AF_INET, &ipaddr),
274					    (buf[2] << 8) | buf[3]);
275					cp += strlen(cp);
276				}
277				/* reply version, no SOCKS version in v4 */
278				put_socks4_res(cp, buf[1]);
279				break;
280			case 5:
281				(void) sprintf(cp, "SOCKS5 method accepted:");
282				cp += strlen(cp);
283				put_method(cp, buf[1]);
284				break;
285			default:
286				(void) sprintf(cp, "SOCKS (recv data)");
287			}
288		} else
289			(void) sprintf(cp, "SOCKS (recv data)");
290	}
291
292	if (flags & F_DTAIL) {
293		show_header("SOCKS: ", "SOCKS Header", fraglen);
294		show_space();
295		cp = get_line(0, 0);
296		if (fraglen >= 2) {
297			switch (buf[0]) {
298			case 0:
299				/* reply version, no SOCKS version in v4 */
300				(void) sprintf(cp,
301				    "Reply version = 0 (SOCKS version 4)");
302				if (fraglen >= 8) {
303					(void) memcpy(&ipaddr, &buf[4],
304					    sizeof (ipaddr));
305					(void) sprintf(get_line(0, 0),
306					    "Destination %s:%u ",
307					    addrtoname(AF_INET, &ipaddr),
308					    (buf[2] << 8) | buf[3]);
309				}
310				cp = get_line(0, 0);
311				(void) sprintf(cp, "Result code = %u ", buf[1]);
312				cp += strlen(cp);
313				put_socks4_res(cp, buf[1]);
314				break;
315			case 5:
316				(void) sprintf(cp, "Reply version = 5");
317				if (fraglen == 2) {
318					cp = get_line(0, 0);
319					(void) sprintf(cp, "Method accepted =");
320					cp += strlen(cp);
321					put_method(cp, buf[1]);
322				} else if (fraglen >= 6 && buf[2] == 0x00) {
323					cp = get_line(0, 0);
324					(void) sprintf(cp, "Status = ");
325					cp += strlen(cp);
326					put_socks5_res(cp, buf[1]);
327					put_socks5_addr(get_line(0, 0),
328					    &buf[3], fraglen - 3);
329				}
330				break;
331			default:
332				(void) sprintf(cp, "(recv data)");
333			}
334		} else
335			(void) sprintf(cp, "(recv data)");
336		show_space();
337	}
338
339out:
340	return (fraglen);
341}
342
343static void
344put_method(char *cp, int method)
345{
346	switch (method) {
347	case 0:
348		(void) sprintf(cp, " NOAUTH");
349		break;
350	case 1:
351		(void) sprintf(cp, " GSSAPI");
352		break;
353	case 2:
354		(void) sprintf(cp, " USERNAME/PASSWD");
355		break;
356	case 255:
357		(void) sprintf(cp, " NONE");
358		break;
359	default:
360		(void) sprintf(cp, " 0x%02x (unknown)", method);
361	}
362}
363
364static void
365put_socks5_addr(char *cp, const unsigned char *buf, int fraglen)
366{
367	struct in_addr	ipaddr;
368	int		i;
369
370	switch (buf[0]) {
371	case 1:
372		/* IPv4 */
373		(void) sprintf(cp, "Address = ");
374		cp += strlen(cp);
375		if (1 + 4 + 2 <= fraglen) {
376			(void) memcpy(&ipaddr, &buf[1], sizeof (ipaddr));
377			(void) sprintf(cp, "%s:%u",
378			    addrtoname(AF_INET, &ipaddr),
379			    (buf[5] << 8) | buf[5 + 1]);
380		} else
381			(void) strcat(cp, "(IPv4)");
382		break;
383	case 3:
384		/* domain name */
385		(void) sprintf(cp, "Domain name = ");
386		cp += strlen(cp);
387		for (i = 0; i <= buf[1] && 1 + i < fraglen; ++i)
388			*cp++ = buf[1 + i];
389		if (1 + i + 2 <= fraglen)
390			(void) sprintf(cp, ":%u",
391			    (buf[1 + i] << 8) | buf[1 + i + 1]);
392		else
393			*cp = '\0';
394		break;
395	case 4:
396		/* IPv6 */
397		(void) sprintf(cp, "Address = ");
398		if (1 + 16 <= fraglen) {
399			for (i = 0; i < 16; ++i) {
400				if (i > 0)
401					*cp++ = '.';
402				(void) sprintf(cp, "%u", buf[1 + i]);
403				cp += strlen(cp);
404			}
405			if (1 + 16 + 2 <= fraglen) {
406				(void) sprintf(cp, ":%u",
407				    (buf[1 + 16] << 8) | buf[1 + 16 + 1]);
408			}
409		} else
410			(void) strcat(cp, "(IPv6)");
411		break;
412	default:
413		(void) sprintf(cp, "Address type = 0x%02x (unknown)", buf[0]);
414	}
415}
416
417static void
418put_socks4_res(char *cp, int code)
419{
420	switch (code) {
421	case 90:
422		(void) sprintf(cp, "request granted");
423		break;
424	case 91:
425		(void) sprintf(cp, "request rejected or failed");
426		break;
427	case 92:
428		(void) sprintf(cp, "socksd can't connect to client's identd");
429		break;
430	case 93:
431		(void) sprintf(cp, "identity mismatch");
432		break;
433	default:
434		(void) sprintf(cp, "0x%02x (unknown)", code);
435	}
436}
437
438static void
439put_socks5_res(char *cp, int code)
440{
441	switch (code) {
442	case 0:
443		(void) strcpy(cp, "succeeded");
444		break;
445	case 1:
446		(void) strcpy(cp, "general SOCKS server failure");
447		break;
448	case 2:
449		(void) strcpy(cp, "connection not allowed by ruleset");
450		break;
451	case 3:
452		(void) strcpy(cp, "network unreachable");
453		break;
454	case 4:
455		(void) strcpy(cp, "host unreachable");
456		break;
457	case 5:
458		(void) strcpy(cp, "connection refused");
459		break;
460	case 6:
461		(void) strcpy(cp, "TTL expired");
462		break;
463	case 7:
464		(void) strcpy(cp, "command not supported");
465		break;
466	case 8:
467		(void) strcpy(cp, "address type not supported");
468		break;
469	default:
470		(void) sprintf(cp, "code 0x%02x", code);
471	}
472}
473