subfinder / research

❄️ Research project for SubFinder core API V2
MIT License
35 stars 8 forks source link

Investigate better Result #1

Closed picatz closed 6 years ago

picatz commented 6 years ago

I made a simple Result type in the master branch of this repository. It's a very simple implementation that I would like to improve and ensure it's robust enough to handle what we want.

The purpose of this specific type is to carry along the general information found during the subdomain enumeration process, encapsulating the necessary logic into a concise protocol/interface which other pieces of the internal system and work with.

At least that's what I was thinking when making it. 😹

Current Implementation

It currently looks like this (note: this is not used in SubFinder, just this repository):

type Result struct {
    Type    string
    Success string
    Failure error
}

I'm fairly certain this can be improved to provide better... results 😉

Forgive my terrible puns, please.

Improving the Implementation

Note these are not in order or need to be done together in any way, just thinking:

picatz commented 6 years ago

Success with interface{} (empty interface)

Instead of a string to encapsulate all successes, an empty interface (interface{}) could be used. This would allow different types of results to provide practically any value; and it would be up to whoever is using that type to do the correct type assertion for what they want.

To be clear: I'm not saying this is the best way to go, or the worst.

package main

import "fmt"
import "encoding/json"

type Result struct {
    Type    string
    Success interface{}
    Failure error
}

func main() {
    r := &Result{}
    b := []byte(`{"Type":"example","Success":0,"Failure": null}`)
    err := json.Unmarshal(b, r)
    if err != nil {
        panic(err)
    }
    r.Success = r.Success.(float64) + 1
    fmt.Println(r.Success == float64(1))
}

Output

true

Note: this also provides the least amount of friction for what is current implemented. Though I do not think that is the primary "selling point" for this approach. Just one aspect to consider. 👍

Ice3man543 commented 6 years ago

Implementing success as interface is really cool. It would also allow us to return an array of subdomains or individual subdomains.

picatz commented 6 years ago

@Ice3man543 Yeah, I agree. 👍

Example of dealing with results with different underly success types (string and slice of strings):

package main

import "fmt"

type Result struct {
    Type    string
    Success interface{}
    Failure error
}

var Example1 = &Result{Type: "example1", Success: "red.google.com"}
var Example2 = &Result{Type: "example2", Success: []string{"gree.google.com", "blue.google.com"}}

func main() {
    examples := []*Result{Example1, Example2}

    for _, example := range examples {
        switch v := example.Success.(type) {
        case string:
            fmt.Printf("%v is a string\n", v)
        case []string:
            fmt.Printf("%v is a slice of strings\n", v)
        default:
            fmt.Printf("The type of v is unknown\n")
        }
    }
}

Output

red.google.com is a string
[gree.google.com blue.google.com] is a slice of strings
picatz commented 6 years ago

Timestamp (UTC)?

Just another thought, maybe not the best one. 🤷‍♀️ What if there was a timestamp associated with the result? Something like Coordinated Universal Time (UTC).

In certain situations, this may be useful to have such as:

picatz commented 6 years ago

RWMutex?

To allow for safe(?), concurrent access from multiple go routines on a single Result, a simple RWMutex synchronization primitive could be used.

picatz commented 6 years ago

Over in the investigate better result branch, I've implemented the following:

type Result struct {
    sync.RWMutex
    Timestamp time.Time
    Type      string
    Success   interface{}
    Failure   error
}