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");