Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor codebase, also removing some features #14

Merged
merged 26 commits into from
Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language: go
go:
- 1.13.x

script: make test
92 changes: 18 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
[![GoDoc](https://godoc.org/github.com/booster-proj/lsaddr?status.svg)](https://godoc.org/github.com/booster-proj/lsaddr)
[![Go Report Card](https://goreportcard.com/badge/github.com/booster-proj/lsaddr)](https://goreportcard.com/report/github.com/booster-proj/lsaddr)
[![Release](https://img.shields.io/github/release/booster-proj/lsaddr.svg)](https://github.com/booster-proj/lsaddr/releases/latest)
[![Build Status](https://travis-ci.org/jecoz/lsaddr.svg?branch=master)](https://travis-ci.org/jecoz/lsaddr)
[![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)

## Before we start
#### Supported OS
- `macOS`
- `linux`
- `windows` (**NEW** 💥)
- `windows

#### External dependencies
OS | Dep | Notes
Expand All @@ -27,85 +29,27 @@ Choose one
Big thanks to [goreleaser](https://github.com/goreleaser/goreleaser) and [godownloader](https://github.com/goreleaser/godownloader) which made the releasing process **FUN**! 🤩

## Usage
The idea is to easily filter the list of open network files of a specific application. The list is filtered with a regular expression: only
the lines that match against it are kept, the others discarded. You can pass to `lsaddr` either directly the regex, or the root folder of the
target app (supported only on macOS for now). Check out some examples:
The idea is to easily filter the list of open network files of a specific application. The list is filtered with a regular expression: only the lines that match against it are kept, the others discarded.


#### Example #1
"Spotify" is used as a regular expression.
```
$ bin/lsaddr Spotify
COMMAND,NET,SRC,DST
Spotify,tcp,192.168.0.98:54862,104.199.64.69:4070
Spotify,tcp,*:57621,
Spotify,tcp,*:54850,
Spotify,udp,*:57621,
Spotify,udp,*:1900,
Spotify,udp,*:61152,
Spotify,udp,*:51535,
Spotify,tcp,192.168.0.98:54878,35.186.224.47:443
Spotify,tcp,192.168.0.98:54872,35.186.224.53:443
```

#### Example #2
"/Applications/Spotify.app" is used to find the application's name, then its
process identifiers are used to build the regular expression.
## Examples
#### Find connections opened by "Spotify"
```
$ bin/lsaddr /Applications/Spotify.app/
COMMAND,NET,SRC,DST
Spotify,tcp,192.168.0.98:54862,104.199.64.69:4070
Spotify,tcp,*:57621,
Spotify,tcp,*:54850,
Spotify,udp,*:57621,
Spotify,udp,*:1900,
Spotify,udp,*:61152,
Spotify,udp,*:51535,
Spotify,tcp,192.168.0.98:54878,35.186.224.47:443
Spotify,tcp,192.168.0.98:54872,35.186.224.53:443
% bin/lsaddr Spotify
PID,CMD,NET,SRC,DST
62822,Spotify,tcp,10.7.152.118:52213,104.199.64.50:80
62822,Spotify,tcp,10.7.152.118:52255,35.186.224.47:443
62826,Spotify,tcp,10.7.152.118:52196,35.186.224.53:443
```

#### Example #3
`--debug` information is printed to `stderr`, command's output to `stdout`.
#### Increment verbosity (debugging)
Note: `debug` information is printed to `stderr`, command's output to `stdout`.
```
$ bin/lsaddr /Applications/Spotify.app/ --debug
[lsaddr] 2019/07/12 14:29:50 app name: Spotify, path: /Applications/Spotify.app
[lsaddr] 2019/07/12 14:29:50 regexp built: "48042|48044|48045|48047"
[lsaddr] 2019/07/12 14:29:50 # of open files: 9
COMMAND,NET,SRC,DST
Spotify,tcp,192.168.0.98:54862,104.199.64.69:4070
Spotify,tcp,*:57621,
Spotify,tcp,*:54850,
Spotify,udp,*:57621,
Spotify,udp,*:1900,
Spotify,udp,*:61152,
Spotify,udp,*:51535,
Spotify,tcp,192.168.0.98:54878,35.186.224.47:443
Spotify,tcp,192.168.0.98:54872,35.186.224.53:443
% bin/lsaddr Spotify --verbose
...
```
(output omitted for readiness)

#### Example #4
- you can encode the output either in csv or as a [bpf](https://en.wikipedia.org/wiki/Berkeley_Packet_Filter) (hint: very useful for packet capturing tools).
- only the unique destination addresses are taken into consideration when building the filter,
ignoring the ports and without specifing if the "direction" (incoming or outgoing) that we want to
filter. This is because the expected behaviour has not yet been defined.
```
$ bin/lsaddr /Applications/Mail.app --out=bpf
(tcp and host 192.168.0.98 and port 58100) or (tcp and host 64.233.184.108 and port 993) or (tcp and host 192.168.0.98 and port 58100) or (tcp and host 64.233.184.108 and port 993) or (tcp and host 192.168.0.98 and port 57213) or (tcp and host 10.0.0.1 and port 993) or (tcp and host 192.168.0.98 and port 57213) or (tcp and host 10.0.0.1 and port 993) or (tcp and host 192.168.0.98 and port 57214) or (tcp and host 10.0.0.1 and port 993) or (tcp and host 192.168.0.98 and port 57214) or (tcp and host 10.0.0.1 and port 993) or (tcp and host 192.168.0.98 and port 57216) or (tcp and host 17.56.136.197 and port 993) or (tcp and host 192.168.0.98 and port 57216) or (tcp and host 17.56.136.197 and port 993) or (tcp and host 192.168.0.98 and port 57217) or (tcp and host 17.56.136.197 and port 993) or (tcp and host 192.168.0.98 and port 57217) or (tcp and host 17.56.136.197 and port 993)
```
#### Example #5
At the moment on Windows you can pass the absulute path of the program you want (or straight `<program>.exe`)
to analyze.
#### Dump Spotify's network traffic using tcpdump
```
> lsaddr.exe "chrome.exe"
COMMAND,NET,SRC,DST
chrome.exe,tcp,10.211.55.3:50551,216.58.205.163:443
chrome.exe,tcp,10.211.55.3:50556,216.58.205.195:443
chrome.exe,tcp,10.211.55.3:50558,216.58.205.67:443
chrome.exe,tcp,10.211.55.3:50567,216.58.205.106:443
chrome.exe,udp,0.0.0.0:5353,*:*
chrome.exe,udp,0.0.0.0:5353,*:*
chrome.exe,udp,0.0.0.0:5353,*:*
chrome.exe,udp,[::]:5353,*:*
chrome.exe,udp,[::]:5353,*:*
% bin/lsaddr -f bpf Spotify | xargs -0 sudo tcpdump
```
38 changes: 20 additions & 18 deletions cmd/version.go → bpf/encoder.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Copyright © 2019 booster authors
//
// Copyright © 2019 Jecoz
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
Expand All @@ -13,29 +12,32 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package cmd
package bpf

import (
"fmt"
"io"

"github.com/spf13/cobra"
"github.com/jecoz/lsaddr/onf"
)

var (
Version = "N/A"
Commit = "N/A"
BuildTime = "N/A"
)
type Encoder struct {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type Encoder should have comment or be unexported

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type Encoder should have comment or be unexported

w io.Writer
}

// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "print version information",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Version: %s, Commit: %s, Built at: %s\n\n", Version, Commit, BuildTime)
},
func NewEncoder(w io.Writer) *Encoder {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported function NewEncoder should have comment or be unexported

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported function NewEncoder should have comment or be unexported

return &Encoder{w: w}
}

func init() {
rootCmd.AddCommand(versionCmd)
func (e *Encoder) Encode(set []onf.ONF) error {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported method Encoder.Encode should have comment or be unexported

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported method Encoder.Encode should have comment or be unexported

var expr Expr
for _, v := range set {
src := string(FromAddr(NODIR, v.Src).Wrap())
dst := string(FromAddr(NODIR, v.Dst).Wrap())
expr = expr.Or(src).Or(dst)
}
if _, err := io.Copy(e.w, expr.NewReader()); err != nil {
return fmt.Errorf("unable to encode open network files: %w", err)
}
return nil
}
2 changes: 1 addition & 1 deletion bpf/expr.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2019 booster authors
// Copyright © 2019 Jecoz
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
Expand Down
4 changes: 2 additions & 2 deletions bpf/expr_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2019 booster authors
// Copyright © 2019 Jecoz
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
Expand All @@ -18,7 +18,7 @@ import (
"strings"
"testing"

"github.com/booster-proj/lsaddr/bpf"
"github.com/jecoz/lsaddr/bpf"
)

func TestJoin(t *testing.T) {
Expand Down
128 changes: 62 additions & 66 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2019 booster authors
// Copyright © 2019 Jecoz
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
Expand All @@ -23,55 +23,90 @@ import (
"os"
"strings"

"github.com/booster-proj/lsaddr/bpf"
"github.com/booster-proj/lsaddr/csv"
"github.com/booster-proj/lsaddr/lookup"
"github.com/jecoz/lsaddr/bpf"
"github.com/jecoz/lsaddr/csv"
"github.com/jecoz/lsaddr/onf"
"github.com/spf13/cobra"
)

var debug bool
var output string
// Build information.
var (
Version = "N/A"
Commit = "N/A"
BuildTime = "N/A"
)

// Flags.
var (
verbose bool
version bool
format string
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "lsaddr",
Short: "Show a subset of all network addresses being used by the system",
Short: "List used network addresses.",
Long: usage,
Args: cobra.MaximumNArgs(1),
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.SetPrefix("[lsaddr] ")
if !debug {
if !verbose {
log.SetOutput(ioutil.Discard)
}
},
Run: func(cmd *cobra.Command, args []string) {
if version {
fmt.Printf("Version: %s, Commit: %s, Built at: %s\n\n", Version, Commit, BuildTime)
os.Exit(0)
}
w := bufio.NewWriter(os.Stdout)
enc, err := newEncoder(w, format)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}

output = strings.ToLower(output)
if err := validateOutput(output); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
set, err := onf.FetchAll()
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
},
Run: func(cmd *cobra.Command, args []string) {
var s string
pivot := "*"
if len(args) > 0 {
s = args[0]
pivot = args[0]
}

ff, err := lookup.OpenNetFiles(s)
set, err = onf.Filter(set, pivot)
if err != nil {
fmt.Printf("unable to find open network files for %s: %v\n", s, err)
fmt.Fprintf(os.Stderr, "error: unable to filter with %s: %v\n", pivot, err)
os.Exit(1)
}
log.Printf("# of open files: %d", len(ff))

w := bufio.NewWriter(os.Stdout)
if err := writeOutputTo(w, output, ff); err != nil {
fmt.Fprintf(os.Stderr, "unable to write output: %v", err)
log.Printf("# of open network files: %d", len(set))
if err := enc.Encode(set); err != nil {
fmt.Fprintf(os.Stderr, "error: unable to encode output: %v\n", err)
os.Exit(1)
}
w.Flush()
os.Exit(0)
},
}

type Encoder interface {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type Encoder should have comment or be unexported

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type Encoder should have comment or be unexported

Encode([]onf.ONF) error
}

func newEncoder(w io.Writer, format string) (Encoder, error) {
switch strings.ToLower(format) {
case "csv":
return csv.NewEncoder(w), nil
case "bpf":
return bpf.NewEncoder(w), nil
default:
return nil, fmt.Errorf("unrecognised format option %s", format)
}
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
Expand All @@ -82,53 +117,14 @@ func Execute() {
}

func init() {
rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "", false, "increment logger verbosity")
rootCmd.PersistentFlags().StringVarP(&output, "out", "o", "csv", "choose type of output produced <csv|bpf>")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Increment logger verbosity.")
rootCmd.PersistentFlags().BoolVarP(&version, "version", "", false, "Print build information such as version, commit and build time.")
rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "csv", "Choose output format.")
}

func writeOutputTo(w io.Writer, output string, ff []lookup.NetFile) error {
switch output {
case "csv":
return csv.NewEncoder(w).Encode(ff)
case "bpf":
var expr bpf.Expr
for _, v := range ff {
src := string(bpf.FromAddr(bpf.NODIR, v.Src).Wrap())
dst := string(bpf.FromAddr(bpf.NODIR, v.Dst).Wrap())
expr = expr.Or(src).Or(dst)
}
_, err := io.Copy(w, expr.NewReader())
return err
}
return nil
}

func validateOutput(output string) error {
switch output {
case "csv", "bpf":
return nil
default:
return fmt.Errorf("unrecognised output %s", output)
}
}

const usage = `
'lsaddr' takes the entire list of currently open network connections and filters it out
using the argument provided, which can either be:

- "*.app" (macOS): It will be recognised as the path leading to the root directory of
an Application. The tool will then:
1. Extract the Application's CFBundleExecutable value
2. Use it to find the list of Pids associated with the program
3. Build a regular expression out of them

- a regular expression: which will be used to filter out the list of open files. Each line that
does not match against the regex will be discarded. On macOS, the list of open files is fetched
using 'lsof -i -n -P'.
Check out https://golang.org/pkg/regexp/ to learn how to properly format your regex.
const usage = `List open network connections. Results can be filtered passing a raw regular expression as argument (check out https://golang.org/pkg/regexp/ to learn how to properly format your regex).

It is possible to configure with the '-out' flag which output 'lsaddr' will produce. Possible
values are:
Using the "--format" or "-f" flag, it is possible to decide the format/encoding of the output produced. Possible values are:
- "bpf": produces a Berkley Packet Filter expression, which, if given to a tool that supports
bpfs, will make it capture only the packets headed to/coming from the destination addresses
of the open network files collected.
Expand Down
Loading