-
Notifications
You must be signed in to change notification settings - Fork 15
/
tcpuid_linux.go
155 lines (133 loc) · 4.13 KB
/
tcpuid_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* ipp-usb - HTTP reverse proxy, backed by IPP-over-USB connection to device
*
* Copyright (C) 2020 and up by Alexander Pevzner ([email protected])
* See LICENSE for license terms and conditions
*
* UID discovery for TCP connection over loopback -- Linux version
*/
package main
import (
"encoding/binary"
"fmt"
"net"
"syscall"
"unsafe"
)
// #include <linux/inet_diag.h>
// #include <linux/in.h>
// #include <linux/netlink.h>
// #include <linux/sock_diag.h>
// #include <netinet/tcp.h>
// #include <stdint.h>
// #include <sys/socket.h>
//
// typedef struct inet_diag_req_v2 inet_diag_req_v2_struct;
// typedef struct inet_diag_sockid inet_diag_sockid_struct;
// typedef struct nlmsgerr nlmsgerr_struct;
// typedef struct inet_diag_msg inet_diag_msg_struct;
//
// typedef struct {
// struct nlmsghdr hdr;
// struct inet_diag_req_v2 data;
// } sock_diag_request;
//
import "C"
// TCPClientUIDSupported tells if TCPClientUID supported on this platform
func TCPClientUIDSupported() bool {
return true
}
// TCPClientUID obtains UID of client process that created
// TCP connection over the loopback interface
func TCPClientUID(client, server *net.TCPAddr) (int, error) {
// Obtain protocol family. Check for mismatch.
clientIs4 := client.IP.To4() != nil
serverIs4 := server.IP.To4() != nil
if clientIs4 != serverIs4 {
return -1, fmt.Errorf("TCPClientUID: IP4/IP6 mismatchh")
}
// Open NETLINK_SOCK_DIAG socket
sock, err := sockDiagOpen()
if err != nil {
return -1, err
}
defer syscall.Close(sock)
// Prepare request
rq := C.sock_diag_request{}
rq.hdr.nlmsg_len = C.uint32_t(unsafe.Sizeof(rq))
rq.hdr.nlmsg_type = C.uint16_t(C.SOCK_DIAG_BY_FAMILY)
rq.hdr.nlmsg_flags = C.uint16_t(C.NLM_F_REQUEST)
if clientIs4 {
rq.data.sdiag_family = C.AF_INET
copy((*[16]byte)(unsafe.Pointer(&rq.data.id.idiag_src))[:],
client.IP.To4())
copy((*[16]byte)(unsafe.Pointer(&rq.data.id.idiag_dst))[:],
server.IP.To4())
} else {
rq.data.sdiag_family = C.AF_INET6
copy((*[16]byte)(unsafe.Pointer(&rq.data.id.idiag_src))[:],
client.IP.To16())
copy((*[16]byte)(unsafe.Pointer(&rq.data.id.idiag_dst))[:],
server.IP.To16())
}
rq.data.sdiag_protocol = C.IPPROTO_TCP
rq.data.idiag_states = 1 << C.TCP_ESTABLISHED
rq.data.id.idiag_sport = C.uint16_t(toBE16((uint16(client.Port))))
rq.data.id.idiag_dport = C.uint16_t(toBE16((uint16(server.Port))))
rq.data.id.idiag_cookie[0] = C.INET_DIAG_NOCOOKIE
rq.data.id.idiag_cookie[1] = C.INET_DIAG_NOCOOKIE
// Send request
rqData := (*[unsafe.Sizeof(rq)]byte)(unsafe.Pointer(&rq))
rqAddr := &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}
err = syscall.Sendto(sock, rqData[:], 0, rqAddr)
if err != nil {
return -1, fmt.Errorf("sock_diag: sendto(): %s", err)
}
// Receive responses
buf := make([]byte, syscall.Getpagesize())
for {
num, _, err := syscall.Recvfrom(sock, buf, 0)
if err != nil {
return -1, fmt.Errorf("sock_diag: recvfrom(): %s", err)
}
msgs, err := syscall.ParseNetlinkMessage(buf[:num])
if err != nil {
return -1, fmt.Errorf("sock_diag: can't parse response")
}
for _, msg := range msgs {
data := unsafe.Pointer(&msg.Data[0])
switch msg.Header.Type {
case syscall.NLMSG_ERROR:
rsp := (*C.nlmsgerr_struct)(data)
err = syscall.Errno(-rsp.error)
err = fmt.Errorf("NLMSG_ERROR: %s", err)
return -1, err
case uint16(C.SOCK_DIAG_BY_FAMILY):
rsp := (*C.inet_diag_msg_struct)(data)
return int(rsp.idiag_uid), nil
}
}
}
}
// sockDiagOpen opens NETLINK_SOCK_DIAG socket
func sockDiagOpen() (int, error) {
const stype = syscall.SOCK_DGRAM | syscall.SOCK_CLOEXEC
const proto = int(C.NETLINK_SOCK_DIAG)
sock, err := syscall.Socket(syscall.AF_NETLINK, stype, proto)
if err != nil {
return -1, fmt.Errorf("sock_diag: socket(): %s", err)
}
sa := &syscall.SockaddrNetlink{Family: syscall.AF_NETLINK}
err = syscall.Bind(sock, sa)
if err != nil {
syscall.Close(sock)
return -1, fmt.Errorf("sock_diag: bind(): %s", err)
}
return sock, nil
}
// toBE16 converts uint16 to big endian
func toBE16(in uint16) uint16 {
var out uint16
p := (*[2]byte)(unsafe.Pointer(&out))
binary.BigEndian.PutUint16(p[:], in)
return out
}