diff --git a/build.pl b/build.pl index 8ae4e89b1..d1794a29d 100755 --- a/build.pl +++ b/build.pl @@ -409,6 +409,7 @@ TARGET: lib/go/camli/magic TARGET: lib/go/camli/misc/httprange TARGET: lib/go/camli/misc/amazon/s3 TARGET: lib/go/camli/mysqlindexer +TARGET: lib/go/camli/netutil TARGET: lib/go/camli/schema TARGET: lib/go/camli/search TARGET: lib/go/camli/test diff --git a/lib/go/camli/netutil/ident.go b/lib/go/camli/netutil/ident.go new file mode 100644 index 000000000..d8ab245ae --- /dev/null +++ b/lib/go/camli/netutil/ident.go @@ -0,0 +1,134 @@ +/* +Copyright 2011 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package netutil + +import ( + "bufio" + "fmt" + "net" + "io" + "log" + "os" + "strconv" + "strings" +) + +var _ = log.Printf + +// TODO: Linux-specific right now. +// Returns os.ENOENT on not found. +func ConnUserid(conn net.Conn) (uid int, err os.Error) { + return AddrPairUserid(conn.LocalAddr().String(), conn.RemoteAddr().String()) +} + +func splitIPPort(param, value string) (ip net.IP, port int, reterr os.Error) { + addrs, ports, err := net.SplitHostPort(value) + if err != nil { + reterr = fmt.Errorf("netutil: AddrPairUserid invalid %s value: %v", err) + return + } + ip = net.ParseIP(addrs) + if ip == nil { + reterr = fmt.Errorf("netutil: invalid %s IP %q", param, addrs) + return + } + port, err = strconv.Atoi(ports) + if err != nil || port <= 0 || port > 65535 { + reterr = fmt.Errorf("netutil: invalid port %q", ports) + return + } + return +} + +// AddrPairUserid returns the local userid who owns the TCP connection +// given by the local and remote ip:port (lipport and ripport, +// respectively). Returns os.ENOENT for the error if the TCP connection +// isn't found. +func AddrPairUserid(lipport, ripport string) (uid int, err os.Error) { + lip, lport, err := splitIPPort("lipport", lipport) + if err != nil { + return -1, err + } + rip, rport, err := splitIPPort("ripport", ripport) + if err != nil { + return -1, err + } + localv4 := (lip.To4() != nil) + remotev4 := (rip.To4() != nil) + if localv4 != remotev4 { + return -1, fmt.Errorf("netutil: address pairs of different families; localv4=%v, remotev4=%v", + localv4, remotev4) + } + + file := "/proc/net/tcp" + if !localv4 { + file = "/proc/net/tcp6" + } + f, err := os.Open(file, os.O_RDONLY, 0) + if err != nil { + return -1, fmt.Errorf("Error opening %s: %v", file, err) + } + defer f.Close() + return uidFromReader(lip, lport, rip, rport, f) +} + +func reverseIPBytes(b []byte) []byte { + rb := make([]byte, len(b)) + for i, v := range b { + rb[len(b) - i - 1] = v + } + return rb +} + +func uidFromReader(lip net.IP, lport int, rip net.IP, rport int, r io.Reader) (uid int, err os.Error) { + buf := bufio.NewReader(r) + + localHex := "" + remoteHex := "" + if lip.To4() != nil { + // In the kernel, the port is run through ntohs(), and + // the inet_request_socket in + // include/net/inet_socket.h says the "loc_addr" and + // "rmt_addr" fields are __be32, but get_openreq4's + // printf of them is raw, without byte order + // converstion. + localHex = fmt.Sprintf("%08X:%04X", reverseIPBytes([]byte(lip.To4())), lport) + remoteHex = fmt.Sprintf("%08X:%04X", reverseIPBytes([]byte(rip.To4())), rport) + } else { + localHex = fmt.Sprintf("%032X:%04X", []byte(lip.To16()), lport) + remoteHex = fmt.Sprintf("%032X:%04X", []byte(rip.To16()), rport) + } + + for { + line, err := buf.ReadString('\n') + if err != nil { + return -1, os.ENOENT + } + parts := strings.Fields(strings.TrimSpace(line)) + if len(parts) < 8 { + continue + } + // log.Printf("parts[1] = %q; localHex = %q", parts[1], localHex) + if parts[1] == localHex && parts[2] == remoteHex { + uid, _ = strconv.Atoi(parts[7]) + return + } + } + panic("unreachable") +} + + diff --git a/lib/go/camli/netutil/ident_test.go b/lib/go/camli/netutil/ident_test.go new file mode 100644 index 000000000..c9b454148 --- /dev/null +++ b/lib/go/camli/netutil/ident_test.go @@ -0,0 +1,62 @@ +/* +Copyright 2011 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package netutil + +import ( + "io/ioutil" + "log" + "net" + "strings" + "testing" +) + +var _ = log.Printf + +// TODO: test IPv6. probably not working. + +func TestIdent4(t *testing.T) { + lip := net.ParseIP("67.218.110.129") + lport := 43436 + rip := net.ParseIP("207.7.148.195") + rport := 80 + + // 816EDA43:A9AC C39407CF:0050 + // 43436 80 + uid, err := uidFromReader(lip, lport, rip, rport, ioutil.NopCloser(strings.NewReader(tcpstat4))) + if err != nil { + t.Error(err) + } + if e, g := 61652, uid; e != g { + t.Errorf("expected uid %d, got %d", e, g) + } +} + +var tcpstat4 = ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode +0: 0100007F:C204 00000000:0000 0A 00000000:00000000 00:00000000 00000000 61652 0 8722922 1 ffff880036b36180 300 0 0 2 -1 +1: 0100007F:0CEA 00000000:0000 0A 00000000:00000000 00:00000000 00000000 120 0 5714729 1 ffff880036b35480 300 0 0 2 -1 +2: 0100007F:2BCB 00000000:0000 0A 00000000:00000000 00:00000000 00000000 65534 0 7381 1 ffff880136370000 300 0 0 2 -1 +3: 0100007F:13AD 00000000:0000 0A 00000000:00000000 00:00000000 00000000 61652 0 4846349 1 ffff880123eb5480 300 0 0 2 -1 +4: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 8307 1 ffff880123eb0d00 300 0 0 2 -1 +5: 00000000:0071 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 8558503 1 ffff88001a242080 300 0 0 2 -1 6: 0100007F:7533 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 8686 1 ffff880136371380 300 0 0 2 -1 +7: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 6015 1 ffff880123eb0680 300 0 0 2 -1 +8: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 8705543 1 ffff88001a242d80 300 0 0 2 -1 +9: 816EDA43:D4DC 35E07D4A:01BB 01 00000000:00000000 02:00000E25 00000000 61652 0 8720744 2 ffff88001a243a80 346 4 24 3 2 +10: 0100007F:C204 0100007F:D981 01 00000000:00000000 00:00000000 00000000 61652 0 8722934 1 ffff88006712a700 21 4 30 5 -1 +11: 816EDA43:A9AC C39407CF:0050 01 00000000:00000000 00:00000000 00000000 61652 0 8754873 1 ffff88006712db00 27 0 0 3 -1 +12: 816EDA43:AFEF 51357D4A:01BB 01 00000000:00000000 02:00000685 00000000 61652 0 8752937 2 ffff880136375480 87 4 2 4 -1 +13: 0100007F:D981 0100007F:C204 01 00000000:00000000 00:00000000 00000000 61652 0 8722933 1 ffff880036b30d00 21 4 0 3 -1 +`