3 * Discover the owner of a connection
5 * (c) 2012 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Yet Another Ident Daemon (YAID).
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.
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.
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.
27 /*----- Header files ------------------------------------------------------*/
35 #include <sys/types.h>
38 #include <arpa/inet.h>
42 #include <mLib/dstr.h>
44 /*----- Data structures ---------------------------------------------------*/
60 struct socket s[NDIR];
64 _(ERROR, U(error, unsigned)) \
65 _(UID, U(uid, uid_t)) \
66 _(NAT, U(nat, struct socket))
69 _(INVPORT, "INVALID-PORT") \
70 _(NOUSER, "NO-USER") \
71 _(HIDDEN, "HIDDEN-USER") \
72 _(UNKNOWN, "UNKNOWN-ERROR")
75 #define DEFENUM(err, tok) E_##err,
82 #define DEFENUM(what, branch) R_##what,
91 #define DEFBRANCH(WHAT, branch) branch
92 #define U(memb, ty) ty memb;
101 /*----- Static variables --------------------------------------------------*/
103 static const char *errtok[] = {
104 #define DEFTOK(err, tok) tok,
109 static int parseaddr4(char **pp, union addr *a)
110 { a->ipv4.s_addr = strtoul(*pp, (char **)pp, 16); return (0); }
112 static int addreq4(const union addr *a, const union addr *aa)
113 { return a->ipv4.s_addr == aa->ipv4.s_addr; }
115 static const struct addrfamily {
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*/ },
126 /*----- Main code ---------------------------------------------------------*/
128 static void dputsock(dstr *d, int af, const struct socket *s)
130 char buf[INET6_ADDRSTRLEN];
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);
138 static void logmsg(const struct query *q, int prio, const char *msg, ...)
144 dputsock(&d, q->af, &q->s[L]);
145 dstr_puts(&d, " <-> ");
146 dputsock(&d, q->af, &q->s[R]);
148 dstr_vputf(&d, msg, &ap);
150 fprintf(stderr, "yaid: %s\n", d.buf);
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); }
158 void identify(const struct query *q, struct response *r)
160 const struct addrfamily *af;
171 #define F_ALL (F_SADDR | F_SPORT | F_DADDR | F_DPORT)
174 enum { LOC, REM, ST, UID, NFIELD };
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);
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));
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; \
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");
201 for (i = 0; i < NFIELD; i++) ff[i] = -1;
204 NEXTFIELD; if (!*p) break;
205 if (strcmp(p, "local_address") == 0)
207 else if (strcmp(p, "rem_address") == 0 ||
208 strcmp(p, "remote_address") == 0)
210 else if (strcmp(p, "uid") == 0)
212 else if (strcmp(p, "st") == 0)
214 else if (strcmp(p, "rx_queue") == 0 ||
215 strcmp(p, "tm->when") == 0)
218 for (i = 0; i < NFIELD; i++) {
220 logmsg(q, LOG_ERR, "failed to find required fields in `%s'",
228 if (dstr_putline(&d, fp) == EOF) break;
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;
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;
258 logmsg(q, LOG_ERR, "failed to read connection table: %s",
263 if (q->af == AF_INET) {
265 if ((fp = fopen("/proc/net/ip_conntrack", "r")) == 0) {
270 "failed to open `/proc/net/ip_conntrack' for reading: %s",
278 if (dstr_putline(&d, fp) == EOF) break;
280 NEXTFIELD; if (!*p) break;
281 if (strcmp(p, "tcp") != 0) continue;
285 NEXTFIELD; if (!*p) break;
286 if (strcmp(p, "ESTABLISHED") == 0)
288 else if (strncmp(p, "src=", 4) == 0) {
289 inet_pton(AF_INET, p + 4, &s[i].addr);
291 } else if (strncmp(p, "dst=", 4) == 0) {
292 inet_pton(AF_INET, p + 4, &s[i + 1].addr);
294 } else if (strncmp(p, "sport=", 6) == 0) {
295 s[i].port = atoi(p + 6);
297 } else if (strncmp(p, "dport=", 6) == 0) {
298 s[i + 1].port = atoi(p + 6);
301 if ((fl & F_ALL) == F_ALL) {
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);
324 if (!(fl & F_ESTAB)) continue;
326 for (i = 0; i < 4; i++)
327 if (sockeq(af, &s[i], &q->s[L])) goto found_local;
331 if (!sockeq(af, &s[i^1], &s[i^2]) ||
332 !sockeq(af, &s[i^1], &q->s[R]))
340 logmsg(q, LOG_ERR, "failed to read `/proc/net/ip_conntrack': %s",
350 r->u.error = E_NOUSER;
354 r->u.error = E_UNKNOWN;
359 /*----- That's all, folks -------------------------------------------------*/
361 int main(int argc, char *argv[])
365 char buf[INET6_ADDRSTRLEN];
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]);
377 printf("uid %d\n", r.u.uid);
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);
384 inet_ntop(q.af, &r.u.nat.addr, buf, sizeof(buf));
385 printf("nat -> %s:%d\n", buf, r.u.nat.port);
388 printf("unknown response\n");