Files: 9bcb2cdd7a920ebc78b59d0b5797d678424aa93a / probe.c
6284 bytesRaw
1 | /* |
2 | # probe.c: Code for probing protocols |
3 | # |
4 | # Copyright (C) 2007-2012 Yves Rutschle |
5 | # |
6 | # This program is free software; you can redistribute it |
7 | # and/or modify it under the terms of the GNU General Public |
8 | # License as published by the Free Software Foundation; either |
9 | # version 2 of the License, or (at your option) any later |
10 | # version. |
11 | # |
12 | # This program is distributed in the hope that it will be |
13 | # useful, but WITHOUT ANY WARRANTY; without even the implied |
14 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
15 | # PURPOSE. See the GNU General Public License for more |
16 | # details. |
17 | # |
18 | # The full text for the General Public License is here: |
19 | # http://www.gnu.org/licenses/gpl.html |
20 | */ |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | static int is_ssh_protocol(const char *p, int len, struct proto*); |
28 | static int is_openvpn_protocol(const char *p, int len, struct proto*); |
29 | static int is_tinc_protocol(const char *p, int len, struct proto*); |
30 | static int is_xmpp_protocol(const char *p, int len, struct proto*); |
31 | static int is_http_protocol(const char *p, int len, struct proto*); |
32 | static int is_true(const char *p, int len, struct proto* proto) { return 1; } |
33 | |
34 | /* Table of protocols that have a built-in probe |
35 | */ |
36 | static struct proto builtins[] = { |
37 | /* description service saddr probe */ |
38 | { "ssh", "sshd", NULL, is_ssh_protocol}, |
39 | { "openvpn", NULL, NULL, is_openvpn_protocol }, |
40 | { "tinc", NULL, NULL, is_tinc_protocol }, |
41 | { "xmpp", NULL, NULL, is_xmpp_protocol }, |
42 | { "http", NULL, NULL, is_http_protocol }, |
43 | { "ssl", NULL, NULL, is_true } |
44 | }; |
45 | |
46 | static struct proto *protocols; |
47 | |
48 | struct proto* get_builtins(void) { |
49 | return builtins; |
50 | } |
51 | |
52 | int get_num_builtins(void) { |
53 | return ARRAY_SIZE(builtins); |
54 | } |
55 | |
56 | /* Returns the protocol to connect to in case of timeout; conventionaly this is |
57 | * the first protocol specified (but maybe we'll make it more explicit some |
58 | * day) |
59 | */ |
60 | struct proto* timeout_protocol(void) { |
61 | return protocols; |
62 | } |
63 | |
64 | /* returns the first protocol (caller can then follow the *next pointers) */ |
65 | struct proto* get_first_protocol(void) |
66 | { |
67 | return protocols; |
68 | } |
69 | |
70 | void set_protocol_list(struct proto* prots) |
71 | { |
72 | protocols = prots; |
73 | } |
74 | |
75 | /* Is the buffer the beginning of an SSH connection? */ |
76 | static int is_ssh_protocol(const char *p, int len, struct proto *proto) |
77 | { |
78 | if (!strncmp(p, "SSH-", 4)) { |
79 | return 1; |
80 | } |
81 | return 0; |
82 | } |
83 | |
84 | /* Is the buffer the beginning of an OpenVPN connection? |
85 | * (code lifted from OpenVPN port-share option) |
86 | */ |
87 | static int is_openvpn_protocol (const char*p,int len, struct proto *proto) |
88 | { |
89 | |
90 | |
91 | if (len >= 3) |
92 | { |
93 | return p[0] == 0 |
94 | && p[1] >= 14 |
95 | && p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT); |
96 | } |
97 | else if (len >= 2) |
98 | { |
99 | return p[0] == 0 && p[1] >= 14; |
100 | } |
101 | else |
102 | return 0; |
103 | } |
104 | |
105 | /* Is the buffer the beginning of a tinc connections? |
106 | * (protocol is undocumented, but starts with "0 " in 1.0.15) |
107 | * */ |
108 | static int is_tinc_protocol( const char *p, int len, struct proto *proto) |
109 | { |
110 | return !strncmp(p, "0 ", 2); |
111 | } |
112 | |
113 | /* Is the buffer the beginning of a jabber (XMPP) connections? |
114 | * (Protocol is documented (http://tools.ietf.org/html/rfc6120) but for lazy |
115 | * clients, just checking first frame containing "jabber" in xml entity) |
116 | * */ |
117 | static int is_xmpp_protocol( const char *p, int len, struct proto *proto) |
118 | { |
119 | return strstr(p, "jabber") ? 1 : 0; |
120 | } |
121 | |
122 | static int probe_http_method(const char *p, const char *opt) |
123 | { |
124 | return !strcmp(p, opt); |
125 | } |
126 | |
127 | /* Is the buffer the beginning of an HTTP connection? */ |
128 | static int is_http_protocol(const char *p, int len, struct proto *proto) |
129 | { |
130 | /* If it's got HTTP in the request (HTTP/1.1) then it's HTTP */ |
131 | if (strstr(p, "HTTP")) |
132 | return 1; |
133 | |
134 | /* Otherwise it could be HTTP/1.0 without version: check if it's got an |
135 | * HTTP method (RFC2616 5.1.1) */ |
136 | probe_http_method(p, "OPTIONS"); |
137 | probe_http_method(p, "GET"); |
138 | probe_http_method(p, "HEAD"); |
139 | probe_http_method(p, "POST"); |
140 | probe_http_method(p, "PUT"); |
141 | probe_http_method(p, "DELETE"); |
142 | probe_http_method(p, "TRACE"); |
143 | probe_http_method(p, "CONNECT"); |
144 | |
145 | return 0; |
146 | } |
147 | |
148 | |
149 | static int regex_probe(const char *p, int len, struct proto *proto) |
150 | { |
151 | regex_t** probe_list = (regex_t**)(proto->data); |
152 | int i=0; |
153 | |
154 | while (probe_list[i]) { |
155 | if (!regexec(probe_list[i], p, 0, NULL, 0)) { |
156 | return 1; |
157 | } |
158 | i++; |
159 | } |
160 | return 0; |
161 | } |
162 | |
163 | /* |
164 | * Read the beginning of data coming from the client connection and check if |
165 | * it's a known protocol. Then leave the data on the defered |
166 | * write buffer of the connection and returns a pointer to the protocol |
167 | * structure |
168 | */ |
169 | struct proto* probe_client_protocol(struct connection *cnx) |
170 | { |
171 | char buffer[BUFSIZ]; |
172 | struct proto *p; |
173 | int n; |
174 | |
175 | n = read(cnx->q[0].fd, buffer, sizeof(buffer)); |
176 | /* It's possible that read() returns an error, e.g. if the client |
177 | * disconnected between the previous call to select() and now. If that |
178 | * happens, we just connect to the default protocol so the caller of this |
179 | * function does not have to deal with a specific failure condition (the |
180 | * connection will just fail later normally). */ |
181 | if (n > 0) { |
182 | defer_write(&cnx->q[1], buffer, n); |
183 | |
184 | for (p = protocols; p; p = p->next) { |
185 | if (p->probe(buffer, n, p)) { |
186 | return p; |
187 | } |
188 | } |
189 | } |
190 | |
191 | /* If none worked, return the first one affected (that's completely |
192 | * arbitrary) */ |
193 | return protocols; |
194 | } |
195 | |
196 | /* Returns the probe for specified protocol: |
197 | * parameter is the description in builtins[], or "regex" |
198 | * */ |
199 | T_PROBE* get_probe(const char* description) { |
200 | int i; |
201 | |
202 | for (i = 0; i < ARRAY_SIZE(builtins); i++) { |
203 | if (!strcmp(builtins[i].description, description)) { |
204 | return builtins[i].probe; |
205 | } |
206 | } |
207 | /* Special case of "regex" probe (we don't want to set it in builtins |
208 | * because builtins is also used to build the command-line options and |
209 | * regexp is not legal on the command line)*/ |
210 | if (!strcmp(description, "regex")) |
211 | return regex_probe; |
212 | |
213 | return NULL; |
214 | } |
215 | |
216 | |
217 |
Built with git-ssb-web