Commit | Line | Data |
---|---|---|
9dcdc856 MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Discover the owner of a connection | |
4 | * | |
5 | * (c) 2012 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Yet Another Ident Daemon (YAID). | |
11 | * | |
12 | * YAID is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * YAID is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with YAID; if not, write to the Free Software Foundation, | |
24 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
25 | */ | |
26 | ||
27 | /*----- Header files ------------------------------------------------------*/ | |
28 | ||
29 | #include <ctype.h> | |
30 | #include <errno.h> | |
31 | #include <stdio.h> | |
32 | #include <string.h> | |
33 | #include <string.h> | |
34 | ||
35 | #include <sys/types.h> | |
36 | #include <unistd.h> | |
37 | ||
38 | #include <arpa/inet.h> | |
39 | ||
40 | #include <syslog.h> | |
41 | ||
42 | #include <mLib/dstr.h> | |
43 | ||
44 | /*----- Data structures ---------------------------------------------------*/ | |
45 | ||
46 | union addr { | |
47 | struct in_addr ipv4; | |
48 | struct in6_addr ipv6; | |
49 | }; | |
50 | ||
51 | struct socket { | |
52 | union addr addr; | |
53 | int port; | |
54 | }; | |
55 | ||
56 | enum { L, R, NDIR }; | |
57 | ||
58 | struct query { | |
59 | int af; | |
60 | struct socket s[NDIR]; | |
61 | } query; | |
62 | ||
63 | #define RESPONSE(_) \ | |
64 | _(ERROR, U(error, unsigned)) \ | |
65 | _(UID, U(uid, uid_t)) \ | |
66 | _(NAT, U(nat, struct socket)) | |
67 | ||
68 | #define ERROR(_) \ | |
69 | _(INVPORT, "INVALID-PORT") \ | |
70 | _(NOUSER, "NO-USER") \ | |
71 | _(HIDDEN, "HIDDEN-USER") \ | |
72 | _(UNKNOWN, "UNKNOWN-ERROR") | |
73 | ||
74 | enum { | |
75 | #define DEFENUM(err, tok) E_##err, | |
76 | ERROR(DEFENUM) | |
77 | #undef DEFENUM | |
78 | E_LIMIT | |
79 | }; | |
80 | ||
81 | enum { | |
82 | #define DEFENUM(what, branch) R_##what, | |
83 | RESPONSE(DEFENUM) | |
84 | #undef DEFENUM | |
85 | R_LIMIT | |
86 | }; | |
87 | ||
88 | struct response { | |
89 | unsigned what; | |
90 | union { | |
91 | #define DEFBRANCH(WHAT, branch) branch | |
92 | #define U(memb, ty) ty memb; | |
93 | #define N | |
94 | RESPONSE(DEFBRANCH) | |
95 | #undef U | |
96 | #undef N | |
97 | #undef DEFBRANCH | |
98 | } u; | |
99 | }; | |
100 | ||
101 | /*----- Static variables --------------------------------------------------*/ | |
102 | ||
103 | static const char *errtok[] = { | |
104 | #define DEFTOK(err, tok) tok, | |
105 | ERROR(DEFTOK) | |
106 | #undef DEFTOK | |
107 | }; | |
108 | ||
109 | static int parseaddr4(char **pp, union addr *a) | |
110 | { a->ipv4.s_addr = strtoul(*pp, (char **)pp, 16); return (0); } | |
111 | ||
112 | static int addreq4(const union addr *a, const union addr *aa) | |
113 | { return a->ipv4.s_addr == aa->ipv4.s_addr; } | |
114 | ||
115 | static const struct addrfamily { | |
116 | int af; | |
117 | const char *procfile; | |
118 | int (*parseaddr)(char **pp, union addr *a); | |
119 | int (*addreq)(const union addr *a, const union addr *aa); | |
120 | } addrfamilytab[] = { | |
121 | { AF_INET, "/proc/net/tcp", parseaddr4, addreq4 }, | |
122 | { AF_INET6, "/proc/net/tcp6", /*parseaddr6*/ }, | |
123 | { -1 } | |
124 | }; | |
125 | ||
126 | /*----- Main code ---------------------------------------------------------*/ | |
127 | ||
128 | static void dputsock(dstr *d, int af, const struct socket *s) | |
129 | { | |
130 | char buf[INET6_ADDRSTRLEN]; | |
131 | ||
132 | inet_ntop(af, &s->addr, buf, sizeof(buf)); | |
133 | if (af != AF_INET6) dstr_puts(d, buf); | |
134 | else { dstr_putc(d, '['); dstr_puts(d, buf); dstr_putc(d, ']'); } | |
135 | dstr_putf(d, ":%d", s->port); | |
136 | } | |
137 | ||
138 | static void logmsg(const struct query *q, int prio, const char *msg, ...) | |
139 | { | |
140 | va_list ap; | |
141 | dstr d = DSTR_INIT; | |
142 | ||
143 | va_start(ap, msg); | |
144 | dputsock(&d, q->af, &q->s[L]); | |
145 | dstr_puts(&d, " <-> "); | |
146 | dputsock(&d, q->af, &q->s[R]); | |
147 | dstr_puts(&d, ": "); | |
148 | dstr_vputf(&d, msg, &ap); | |
149 | va_end(ap); | |
150 | fprintf(stderr, "yaid: %s\n", d.buf); | |
151 | dstr_destroy(&d); | |
152 | } | |
153 | ||
154 | static int sockeq(const struct addrfamily *af, | |
155 | const struct socket *sa, const struct socket *sb) | |
156 | { return (af->addreq(&sa->addr, &sb->addr) && sa->port == sb->port); } | |
157 | ||
158 | void identify(const struct query *q, struct response *r) | |
159 | { | |
160 | const struct addrfamily *af; | |
161 | FILE *fp = 0; | |
162 | dstr d = DSTR_INIT; | |
163 | char *p, *pp; | |
164 | struct socket s[4]; | |
165 | int i; | |
166 | unsigned fl; | |
167 | #define F_SADDR 1u | |
168 | #define F_SPORT 2u | |
169 | #define F_DADDR 4u | |
170 | #define F_DPORT 8u | |
171 | #define F_ALL (F_SADDR | F_SPORT | F_DADDR | F_DPORT) | |
172 | #define F_ESTAB 16u | |
173 | uid_t uid; | |
174 | enum { LOC, REM, ST, UID, NFIELD }; | |
175 | int f, ff[NFIELD]; | |
176 | ||
177 | for (af = addrfamilytab; af->af >= 0; af++) | |
178 | if (af->af == q->af) goto found_af; | |
179 | logmsg(q, LOG_ERR, "unexpected address family `%d'", q->af); | |
180 | goto err_unk; | |
181 | found_af:; | |
182 | ||
183 | if ((fp = fopen(af->procfile, "r")) == 0) { | |
184 | logmsg(q, LOG_ERR, "failed to open `%s' for reading: %s", | |
185 | af->procfile, strerror(errno)); | |
186 | goto err_unk; | |
187 | } | |
188 | ||
189 | #define NEXTFIELD do { \ | |
190 | for (p = pp; isspace((unsigned char)*p); p++); \ | |
191 | for (pp = p; *pp && !isspace((unsigned char)*pp); pp++); \ | |
192 | if (*pp) *pp++ = 0; \ | |
193 | } while (0) | |
194 | ||
195 | if (dstr_putline(&d, fp) == EOF) { | |
196 | logmsg(q, LOG_ERR, "failed to read header line from `%s': %s", | |
197 | af->procfile, ferror(fp) ? strerror(errno) : "unexpected EOF"); | |
198 | goto err_unk; | |
199 | } | |
200 | ||
201 | for (i = 0; i < NFIELD; i++) ff[i] = -1; | |
202 | pp = d.buf; | |
203 | for (f = 0;; f++) { | |
204 | NEXTFIELD; if (!*p) break; | |
205 | if (strcmp(p, "local_address") == 0) | |
206 | ff[LOC] = f; | |
207 | else if (strcmp(p, "rem_address") == 0 || | |
208 | strcmp(p, "remote_address") == 0) | |
209 | ff[REM] = f; | |
210 | else if (strcmp(p, "uid") == 0) | |
211 | ff[UID] = f; | |
212 | else if (strcmp(p, "st") == 0) | |
213 | ff[ST] = f; | |
214 | else if (strcmp(p, "rx_queue") == 0 || | |
215 | strcmp(p, "tm->when") == 0) | |
216 | f--; | |
217 | } | |
218 | for (i = 0; i < NFIELD; i++) { | |
219 | if (ff[i] < 0) { | |
220 | logmsg(q, LOG_ERR, "failed to find required fields in `%s'", | |
221 | af->procfile); | |
222 | goto err_unk; | |
223 | } | |
224 | } | |
225 | ||
226 | for (;;) { | |
227 | DRESET(&d); | |
228 | if (dstr_putline(&d, fp) == EOF) break; | |
229 | pp = d.buf; | |
230 | uid = -1; | |
231 | for (f = 0;; f++) { | |
232 | NEXTFIELD; if (!*p) break; | |
233 | if (f == ff[LOC]) { i = L; goto compare; } | |
234 | else if (f == ff[REM]) { i = R; goto compare; } | |
235 | else if (f == ff[UID]) uid = atoi(p); | |
236 | else if (f == ff[ST]) { | |
237 | if (strtol(p, 0, 16) != 1) goto next_row; | |
238 | } | |
239 | continue; | |
240 | ||
241 | compare: | |
242 | if (af->parseaddr(&p, &s[0].addr)) goto next_row; | |
243 | if (*p != ':') break; p++; | |
244 | s[0].port = strtoul(p, 0, 16); | |
245 | /* FIXME: accept forwarded queries from NAT */ | |
246 | if (!sockeq(af, &q->s[i], &s[0])) goto next_row; | |
247 | else continue; | |
248 | } | |
249 | if (uid != -1) { | |
250 | r->what = R_UID; | |
251 | r->u.uid = uid; | |
252 | goto done; | |
253 | } | |
254 | next_row:; | |
255 | } | |
256 | ||
257 | if (ferror(fp)) { | |
258 | logmsg(q, LOG_ERR, "failed to read connection table: %s", | |
259 | strerror(errno)); | |
260 | goto err_unk; | |
261 | } | |
262 | ||
263 | if (q->af == AF_INET) { | |
264 | fclose(fp); | |
265 | if ((fp = fopen("/proc/net/ip_conntrack", "r")) == 0) { | |
266 | if (errno == ENOENT) | |
267 | goto err_nouser; | |
268 | else { | |
269 | logmsg(q, LOG_ERR, | |
270 | "failed to open `/proc/net/ip_conntrack' for reading: %s", | |
271 | strerror(errno)); | |
272 | goto err_unk; | |
273 | } | |
274 | } | |
275 | ||
276 | for (;;) { | |
277 | DRESET(&d); | |
278 | if (dstr_putline(&d, fp) == EOF) break; | |
279 | pp = d.buf; | |
280 | NEXTFIELD; if (!*p) break; | |
281 | if (strcmp(p, "tcp") != 0) continue; | |
282 | i = 0; | |
283 | fl = 0; | |
284 | for (;;) { | |
285 | NEXTFIELD; if (!*p) break; | |
286 | if (strcmp(p, "ESTABLISHED") == 0) | |
287 | fl |= F_ESTAB; | |
288 | else if (strncmp(p, "src=", 4) == 0) { | |
289 | inet_pton(AF_INET, p + 4, &s[i].addr); | |
290 | fl |= F_SADDR; | |
291 | } else if (strncmp(p, "dst=", 4) == 0) { | |
292 | inet_pton(AF_INET, p + 4, &s[i + 1].addr); | |
293 | fl |= F_DADDR; | |
294 | } else if (strncmp(p, "sport=", 6) == 0) { | |
295 | s[i].port = atoi(p + 6); | |
296 | fl |= F_SPORT; | |
297 | } else if (strncmp(p, "dport=", 6) == 0) { | |
298 | s[i + 1].port = atoi(p + 6); | |
299 | fl |= F_DPORT; | |
300 | } | |
301 | if ((fl & F_ALL) == F_ALL) { | |
302 | fl &= ~F_ALL; | |
303 | if (i < 4) i += 2; | |
304 | else break; | |
305 | } | |
306 | } | |
307 | ||
308 | #ifdef notdef | |
309 | { | |
310 | dstr dd = DSTR_INIT; | |
311 | dstr_putf(&dd, "%sestab ", (fl & F_ESTAB) ? " " : "!"); | |
312 | dputsock(&dd, af->af, &s[0]); | |
313 | dstr_puts(&dd, "<->"); | |
314 | dputsock(&dd, af->af, &s[1]); | |
315 | dstr_puts(&dd, " | "); | |
316 | dputsock(&dd, af->af, &s[2]); | |
317 | dstr_puts(&dd, "<->"); | |
318 | dputsock(&dd, af->af, &s[3]); | |
319 | printf("parsed: %s\n", dd.buf); | |
320 | dstr_destroy(&dd); | |
321 | } | |
322 | #endif | |
323 | ||
324 | if (!(fl & F_ESTAB)) continue; | |
325 | ||
326 | for (i = 0; i < 4; i++) | |
327 | if (sockeq(af, &s[i], &q->s[L])) goto found_local; | |
328 | continue; | |
329 | putchar('.'); | |
330 | found_local: | |
331 | if (!sockeq(af, &s[i^1], &s[i^2]) || | |
332 | !sockeq(af, &s[i^1], &q->s[R])) | |
333 | continue; | |
334 | r->what = R_NAT; | |
335 | r->u.nat = s[i^3]; | |
336 | goto done; | |
337 | } | |
338 | ||
339 | if (ferror(fp)) { | |
340 | logmsg(q, LOG_ERR, "failed to read `/proc/net/ip_conntrack': %s", | |
341 | strerror(errno)); | |
342 | goto err_unk; | |
343 | } | |
344 | } | |
345 | ||
346 | #undef NEXTFIELD | |
347 | ||
348 | err_nouser: | |
349 | r->what = R_ERROR; | |
350 | r->u.error = E_NOUSER; | |
351 | goto done; | |
352 | err_unk: | |
353 | r->what = R_ERROR; | |
354 | r->u.error = E_UNKNOWN; | |
355 | done: | |
356 | dstr_destroy(&d); | |
357 | } | |
358 | ||
359 | /*----- That's all, folks -------------------------------------------------*/ | |
360 | ||
361 | int main(int argc, char *argv[]) | |
362 | { | |
363 | struct query q; | |
364 | struct response r; | |
365 | char buf[INET6_ADDRSTRLEN]; | |
366 | ||
367 | q.af = AF_INET; | |
368 | inet_pton(AF_INET, argv[1], &q.s[L].addr.ipv4); | |
369 | q.s[L].port = atoi(argv[2]); | |
370 | inet_pton(AF_INET, argv[3], &q.s[R].addr.ipv4); | |
371 | q.s[R].port = atoi(argv[4]); | |
372 | ||
373 | identify(&q, &r); | |
374 | ||
375 | switch (r.what) { | |
376 | case R_UID: | |
377 | printf("uid %d\n", r.u.uid); | |
378 | break; | |
379 | case R_ERROR: | |
380 | if (r.u.error < E_LIMIT) printf("error %s\n", errtok[r.u.error]); | |
381 | else printf("error E%u\n", r.u.error); | |
382 | break; | |
383 | case R_NAT: | |
384 | inet_ntop(q.af, &r.u.nat.addr, buf, sizeof(buf)); | |
385 | printf("nat -> %s:%d\n", buf, r.u.nat.port); | |
386 | break; | |
387 | default: | |
388 | printf("unknown response\n"); | |
389 | break; | |
390 | } | |
391 | ||
392 | return (0); | |
393 | } |