commit 0ebc557267e12c3544b0833c6c98a2ea335a58fb
parent 2e02e951df8b4bb89560d1fdc0116d9231290d63
Author: parazyd <parazyd@dyne.org>
Date: Sun, 10 Dec 2017 17:02:45 +0100
Rename lib to damlib and separate functions into more files
Diffstat:
9 files changed, 395 insertions(+), 371 deletions(-)
diff --git a/cmd/dam-client/main.go b/cmd/dam-client/main.go
@@ -13,7 +13,7 @@ import (
"sync"
"time"
- "github.com/parazyd/tor-dam/pkg/lib"
+ lib "github.com/parazyd/tor-dam/pkg/damlib"
)
// Cwd holds the path to the directory where we will Chdir on startup.
diff --git a/cmd/dam-dir/main.go b/cmd/dam-dir/main.go
@@ -13,7 +13,7 @@ import (
"time"
"github.com/go-redis/redis"
- "github.com/parazyd/tor-dam/pkg/lib"
+ lib "github.com/parazyd/tor-dam/pkg/damlib"
)
// Cwd holds the path to the directory where we will Chdir on startup.
diff --git a/pkg/damlib/crypto.go b/pkg/damlib/crypto.go
@@ -0,0 +1,218 @@
+package damlib
+
+// See LICENSE file for copyright and license details.
+
+import (
+ "crypto"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/sha512"
+ "crypto/x509"
+ "encoding/asn1"
+ "encoding/base32"
+ "encoding/pem"
+ "errors"
+ "io/ioutil"
+ "log"
+ "math/big"
+ "os"
+ "strings"
+)
+
+// GenRsa generates a private RSA keypair of a given bitSize int and returns it
+// as rsa.PrivateKey.
+func GenRsa(bitSize int) (*rsa.PrivateKey, error) {
+ log.Printf("Generating %d-bit RSA keypair...\n", bitSize)
+ rng := rand.Reader
+ key, err := rsa.GenerateKey(rng, bitSize)
+ if err != nil {
+ return nil, err
+ }
+ return key, nil
+}
+
+// SavePubRsa saves a given RSA public key to a given filename.
+// SavePubRsa takes the filename to write as a string, and the key as
+// rsa.PublicKey. It returns a boolean value and an error, depending on whether
+// it has failed or not.
+func SavePubRsa(filename string, pubkey rsa.PublicKey) (bool, error) {
+ log.Printf("Writing pubkey to %s\n", filename)
+ // FIXME: worry or not about creating the path if it doesn't exist?
+ outfile, err := os.Create(filename)
+ defer outfile.Close()
+ if err != nil {
+ return false, err
+ }
+ asn1Bytes, err := asn1.Marshal(pubkey)
+ if err != nil {
+ return false, err
+ }
+ var pemkey = &pem.Block{
+ Type: "RSA PUBLIC KEY",
+ Bytes: asn1Bytes,
+ }
+ err = pem.Encode(outfile, pemkey)
+ if err != nil {
+ return false, err
+ }
+ err = outfile.Chmod(0400)
+ if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// SavePrivRsa saves a given RSA private key to a given filename.
+// SavePrivRsa takes the filename to write as a string, and the key as
+// *rsa.PrivateKey. It returns a boolean value and an error, depending on whether
+// it has failed or not.
+func SavePrivRsa(filename string, privkey *rsa.PrivateKey) (bool, error) {
+ log.Printf("Writing private key to %s\n", filename)
+ // FIXME: worry or not about creating the path if it doesn't exist?
+ outfile, err := os.Create(filename)
+ defer outfile.Close()
+ if err != nil {
+ return false, err
+ }
+ var pemkey = &pem.Block{
+ Type: "RSA PRIVATE KEY",
+ Bytes: x509.MarshalPKCS1PrivateKey(privkey),
+ }
+ err = pem.Encode(outfile, pemkey)
+ if err != nil {
+ return false, err
+ }
+ err = outfile.Chmod(0400)
+ if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// LoadRsaKeyFromFile loads a RSA private key from a given filename.
+// LoadRsaKeyFromFile takes a string filename and tries to read from it, parsing
+// the private RSA key. It will return a *rsa.PrivateKey on success, and error
+// on fail.
+func LoadRsaKeyFromFile(filename string) (*rsa.PrivateKey, error) {
+ log.Println("Loading RSA private key from", filename)
+ dat, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ block, _ := pem.Decode(dat)
+ if block == nil {
+ return nil, errors.New("failed to parse PEM block containing the key")
+ }
+ priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
+ if err != nil {
+ return nil, err
+ }
+ return priv, nil
+}
+
+// SignMsgRsa signs a given []byte message using a given RSA private key.
+// It will return the signature as a slice of bytes on success, and error on
+// failure.
+func SignMsgRsa(message []byte, privkey *rsa.PrivateKey) ([]byte, error) {
+ log.Println("Signing message...")
+ rng := rand.Reader
+ hashed := sha512.Sum512(message)
+ sig, err := rsa.SignPKCS1v15(rng, privkey, crypto.SHA512, hashed[:])
+ if err != nil {
+ return nil, err
+ }
+ return sig, nil
+}
+
+// EncryptMsgRsa encrypts a given []byte message using a given RSA public key.
+// Returns the encrypted message as a slice of bytes on success, and error on
+// failure.
+func EncryptMsgRsa(message []byte, pubkey *rsa.PublicKey) ([]byte, error) {
+ log.Println("Encrypting message...")
+ rng := rand.Reader
+ msg, err := rsa.EncryptPKCS1v15(rng, pubkey, message)
+ if err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
+
+// DecryptMsgRsa decrypts a given []byte message using a given RSA private key.
+// Returns the decrypted message as a slice of bytes on success, and error on
+// failure.
+func DecryptMsgRsa(message []byte, privkey *rsa.PrivateKey) ([]byte, error) {
+ log.Println("Decrypting message...")
+ rng := rand.Reader
+ msg, err := rsa.DecryptPKCS1v15(rng, privkey, message)
+ if err != nil {
+ return nil, err
+ }
+ return msg, nil
+}
+
+// VerifyMsgRsa verifies a message and signature against a given RSA pubkey.
+// Returns a boolean value and error depending on whether it has failed or not.
+func VerifyMsgRsa(message []byte, signature []byte, pubkey *rsa.PublicKey) (bool, error) {
+ log.Println("Verifying message signature")
+ hashed := sha512.Sum512(message)
+ err := rsa.VerifyPKCS1v15(pubkey, crypto.SHA512, hashed[:], signature)
+ if err != nil {
+ return false, err
+ }
+ log.Println("Signature valid")
+ return true, nil
+}
+
+// OnionFromPubkeyRsa generates a valid onion address from a given RSA pubkey.
+// Returns the onion address as a slice of bytes on success and error on
+// failure.
+func OnionFromPubkeyRsa(pubkey rsa.PublicKey) ([]byte, error) {
+ asn1Bytes, err := asn1.Marshal(pubkey)
+ if err != nil {
+ return nil, err
+ }
+ hashed := sha1.New()
+ _, err = hashed.Write(asn1Bytes)
+ if err != nil {
+ return nil, err
+ }
+ encoded := strings.ToLower(base32.StdEncoding.EncodeToString(hashed.Sum(nil)))[:16]
+ encoded += ".onion"
+
+ return []byte(encoded), nil
+}
+
+// ParsePubkeyRsa parses a []byte form of a RSA public key and returns it as
+// *rsa.PublicKey on success. Otherwise, error.
+func ParsePubkeyRsa(pubkey []byte) (*rsa.PublicKey, error) {
+ var pub rsa.PublicKey
+ var ret *rsa.PublicKey
+ block, _ := pem.Decode(pubkey)
+ _, err := asn1.Unmarshal(block.Bytes, &pub)
+ if err != nil {
+ return nil, err
+ }
+ ret = &pub
+ return ret, nil
+}
+
+// GenRandomASCII generates a random ASCII string of a given length.
+// Takes length int as argument, and returns a string of that length on success
+// and error on failure.
+func GenRandomASCII(length int) (string, error) {
+ var res string
+ for {
+ if len(res) >= length {
+ return res, nil
+ }
+ num, err := rand.Int(rand.Reader, big.NewInt(int64(127)))
+ if err != nil {
+ return "", err
+ }
+ n := num.Int64()
+ if n > 32 && n < 127 {
+ res += string(n)
+ }
+ }
+}
diff --git a/pkg/damlib/helpers.go b/pkg/damlib/helpers.go
@@ -0,0 +1,15 @@
+package damlib
+
+// See LICENSE file for copyright and license details.
+
+import (
+ "log"
+)
+
+// CheckError is a handler for errors. It takes an error type as an argument,
+// and issues a log.Fatalln, printing the error and exiting with os.Exit(1).
+func CheckError(err error) {
+ if err != nil {
+ log.Fatalln(err)
+ }
+}
diff --git a/pkg/damlib/net.go b/pkg/damlib/net.go
@@ -0,0 +1,55 @@
+package damlib
+
+// See LICENSE file for copyright and license details.
+
+import (
+ "bytes"
+ "log"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "golang.org/x/net/proxy"
+)
+
+// ProxyAddr is the address of our Tor SOCKS port.
+const ProxyAddr = "127.0.0.1:9050"
+
+// HTTPPost sends an HTTP POST request to the given host.
+// Takes the host to request and the data to post as arguments.
+// If the host ends with ".onion", it will enable the request to be performed
+// over a SOCKS proxy, defined in ProxyAddr.
+// On success, it will return the http.Response. Otherwise, it returns an error.
+func HTTPPost(host string, data []byte) (*http.Response, error) {
+ socksify := false
+ parsedHost, err := url.Parse(host)
+ if err != nil {
+ return nil, err
+ }
+ hostname := parsedHost.Hostname()
+ if strings.HasSuffix(hostname, ".onion") {
+ socksify = true
+ }
+ httpTransp := &http.Transport{}
+ httpClient := &http.Client{Transport: httpTransp}
+ if socksify {
+ log.Println("Detected a .onion request. Using SOCKS proxy.")
+ dialer, err := proxy.SOCKS5("tcp", ProxyAddr, nil, proxy.Direct)
+ if err != nil {
+ return nil, err
+ }
+ httpTransp.Dial = dialer.Dial
+ }
+ request, err := http.NewRequest("POST", host, bytes.NewBuffer(data))
+ if err != nil {
+ return nil, err
+ }
+ request.Header.Set("Content-Type", "application/json")
+
+ resp, err := httpClient.Do(request)
+ if err != nil {
+ return nil, err
+ }
+
+ return resp, nil
+}
diff --git a/pkg/damlib/tor.go b/pkg/damlib/tor.go
@@ -0,0 +1,32 @@
+package damlib
+
+// See LICENSE file for copyright and license details.
+
+import (
+ "bytes"
+ "log"
+ "os/exec"
+)
+
+// FetchHSPubkey fetches a hidden service's RSA pubkey by running an external
+// program, giving it an onion address. It returns the retrieved public key as a
+// string.
+func FetchHSPubkey(addr string) string {
+ var outb, errb bytes.Buffer
+
+ log.Println("Fetching pubkey for:", addr)
+
+ cmd := exec.Command("damauth.py", addr)
+ cmd.Stdout = &outb
+ cmd.Stderr = &errb
+ err := cmd.Start()
+ CheckError(err)
+
+ err = cmd.Wait()
+ if err != nil {
+ log.Println("Could not fetch descriptor:", err)
+ return ""
+ }
+
+ return outb.String()
+}
diff --git a/pkg/damlib/validate.go b/pkg/damlib/validate.go
@@ -0,0 +1,73 @@
+package damlib
+
+// See LICENSE file for copyright and license details.
+
+import (
+ "log"
+ "regexp"
+ "strings"
+ "time"
+)
+
+// ValidateReq validates our given request against the logic we are checking.
+// The function takes a request data map, and a public key in the form of a
+// string. If the public key is an empty string, the function will run an
+// external program to fetch the node's public key from a Tor HSDir.
+//
+// ValidateReq will first validate "nodetype", looking whether the announcer
+// is a node or a directory.
+// Then, it will validate the onion address using a regular expression.
+// Now, if pubkey is empty, it will run the external program to fetch it. If a
+// descriptor can't be retrieved, it will retry for 10 times, and fail if those
+// are not successful.
+//
+// Continuing, ValidateReq will verify the RSA signature posted by the
+// announcer.
+// If any of the above are invalid, the function will return nil and false.
+// Otherwise, it will return the pubkey as a slice of bytes, and true.
+func ValidateReq(req map[string]string, pubkey string) ([]byte, bool) {
+ // Validate nodetype.
+ if req["nodetype"] != "node" {
+ return nil, false
+ }
+ // Validate address.
+ re, err := regexp.Compile("^[a-z2-7]{16}\\.onion$")
+ CheckError(err)
+ if len(re.FindString(req["address"])) != 22 {
+ return nil, false
+ }
+ log.Println(req["address"], "seems valid")
+
+ if len(pubkey) == 0 {
+ // Address is valid, we try to fetch its pubkey from a HSDir
+ cnt := 0
+ for { // We try until we have it.
+ cnt++
+ if cnt > 10 {
+ // We probably can't get a good HSDir. The client shall retry
+ // later on.
+ return []byte("Couldn't get a descriptor. Try later."), false
+ }
+ pubkey = FetchHSPubkey(req["address"])
+ if strings.HasPrefix(pubkey, "-----BEGIN RSA PUBLIC KEY-----") &&
+ strings.HasSuffix(pubkey, "-----END RSA PUBLIC KEY-----") {
+ log.Println("Got descriptor!")
+ break
+ }
+ time.Sleep(2000 * time.Millisecond)
+ }
+ }
+ // Validate signature.
+ msg := []byte(req["message"])
+ sig := []byte(req["signature"])
+ pub, err := ParsePubkeyRsa([]byte(pubkey))
+ CheckError(err)
+
+ val, err := VerifyMsgRsa(msg, sig, pub)
+ CheckError(err)
+ if val != true {
+ return nil, false
+ }
+
+ return []byte(pubkey), true
+}
diff --git a/pkg/lib/crypto.go b/pkg/lib/crypto.go
@@ -1,197 +0,0 @@
-package lib
-
-// See LICENSE file for copyright and license details.
-
-import (
- "crypto"
- "crypto/rand"
- "crypto/rsa"
- "crypto/sha1"
- "crypto/sha512"
- "crypto/x509"
- "encoding/asn1"
- "encoding/base32"
- "encoding/pem"
- "errors"
- "io/ioutil"
- "log"
- "os"
- "strings"
-)
-
-// GenRsa generates a private RSA keypair of a given bitSize int and returns it
-// as rsa.PrivateKey.
-func GenRsa(bitSize int) (*rsa.PrivateKey, error) {
- log.Printf("Generating %d-bit RSA keypair...\n", bitSize)
- rng := rand.Reader
- key, err := rsa.GenerateKey(rng, bitSize)
- if err != nil {
- return nil, err
- }
- return key, nil
-}
-
-// SavePubRsa saves a given RSA public key to a given filename.
-// SavePubRsa takes the filename to write as a string, and the key as
-// rsa.PublicKey. It returns a boolean value and an error, depending on whether
-// it has failed or not.
-func SavePubRsa(filename string, pubkey rsa.PublicKey) (bool, error) {
- log.Printf("Writing pubkey to %s\n", filename)
- // FIXME: worry or not about creating the path if it doesn't exist?
- outfile, err := os.Create(filename)
- defer outfile.Close()
- if err != nil {
- return false, err
- }
- asn1Bytes, err := asn1.Marshal(pubkey)
- if err != nil {
- return false, err
- }
- var pemkey = &pem.Block{
- Type: "RSA PUBLIC KEY",
- Bytes: asn1Bytes,
- }
- err = pem.Encode(outfile, pemkey)
- if err != nil {
- return false, err
- }
- err = outfile.Chmod(0400)
- if err != nil {
- return false, err
- }
- return true, nil
-}
-
-// SavePrivRsa saves a given RSA private key to a given filename.
-// SavePrivRsa takes the filename to write as a string, and the key as
-// *rsa.PrivateKey. It returns a boolean value and an error, depending on whether
-// it has failed or not.
-func SavePrivRsa(filename string, privkey *rsa.PrivateKey) (bool, error) {
- log.Printf("Writing private key to %s\n", filename)
- // FIXME: worry or not about creating the path if it doesn't exist?
- outfile, err := os.Create(filename)
- defer outfile.Close()
- if err != nil {
- return false, err
- }
- var pemkey = &pem.Block{
- Type: "RSA PRIVATE KEY",
- Bytes: x509.MarshalPKCS1PrivateKey(privkey),
- }
- err = pem.Encode(outfile, pemkey)
- if err != nil {
- return false, err
- }
- err = outfile.Chmod(0400)
- if err != nil {
- return false, err
- }
- return true, nil
-}
-
-// LoadRsaKeyFromFile loads a RSA private key from a given filename.
-// LoadRsaKeyFromFile takes a string filename and tries to read from it, parsing
-// the private RSA key. It will return a *rsa.PrivateKey on success, and error
-// on fail.
-func LoadRsaKeyFromFile(filename string) (*rsa.PrivateKey, error) {
- log.Println("Loading RSA private key from", filename)
- dat, err := ioutil.ReadFile(filename)
- if err != nil {
- return nil, err
- }
- block, _ := pem.Decode(dat)
- if block == nil {
- return nil, errors.New("failed to parse PEM block containing the key")
- }
- priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
- if err != nil {
- return nil, err
- }
- return priv, nil
-}
-
-// SignMsgRsa signs a given []byte message using a given RSA private key.
-// It will return the signature as a slice of bytes on success, and error on
-// failure.
-func SignMsgRsa(message []byte, privkey *rsa.PrivateKey) ([]byte, error) {
- log.Println("Signing message...")
- rng := rand.Reader
- hashed := sha512.Sum512(message)
- sig, err := rsa.SignPKCS1v15(rng, privkey, crypto.SHA512, hashed[:])
- if err != nil {
- return nil, err
- }
- return sig, nil
-}
-
-// EncryptMsgRsa encrypts a given []byte message using a given RSA public key.
-// Returns the encrypted message as a slice of bytes on success, and error on
-// failure.
-func EncryptMsgRsa(message []byte, pubkey *rsa.PublicKey) ([]byte, error) {
- log.Println("Encrypting message...")
- rng := rand.Reader
- msg, err := rsa.EncryptPKCS1v15(rng, pubkey, message)
- if err != nil {
- return nil, err
- }
- return msg, nil
-}
-
-// DecryptMsgRsa decrypts a given []byte message using a given RSA private key.
-// Returns the decrypted message as a slice of bytes on success, and error on
-// failure.
-func DecryptMsgRsa(message []byte, privkey *rsa.PrivateKey) ([]byte, error) {
- log.Println("Decrypting message...")
- rng := rand.Reader
- msg, err := rsa.DecryptPKCS1v15(rng, privkey, message)
- if err != nil {
- return nil, err
- }
- return msg, nil
-}
-
-// VerifyMsgRsa verifies a message and signature against a given RSA pubkey.
-// Returns a boolean value and error depending on whether it has failed or not.
-func VerifyMsgRsa(message []byte, signature []byte, pubkey *rsa.PublicKey) (bool, error) {
- log.Println("Verifying message signature")
- hashed := sha512.Sum512(message)
- err := rsa.VerifyPKCS1v15(pubkey, crypto.SHA512, hashed[:], signature)
- if err != nil {
- return false, err
- }
- log.Println("Signature valid")
- return true, nil
-}
-
-// OnionFromPubkeyRsa generates a valid onion address from a given RSA pubkey.
-// Returns the onion address as a slice of bytes on success and error on
-// failure.
-func OnionFromPubkeyRsa(pubkey rsa.PublicKey) ([]byte, error) {
- asn1Bytes, err := asn1.Marshal(pubkey)
- if err != nil {
- return nil, err
- }
- hashed := sha1.New()
- _, err = hashed.Write(asn1Bytes)
- if err != nil {
- return nil, err
- }
- encoded := strings.ToLower(base32.StdEncoding.EncodeToString(hashed.Sum(nil)))[:16]
- encoded += ".onion"
-
- return []byte(encoded), nil
-}
-
-// ParsePubkeyRsa parses a []byte form of a RSA public key and returns it as
-// *rsa.PublicKey on success. Otherwise, error.
-func ParsePubkeyRsa(pubkey []byte) (*rsa.PublicKey, error) {
- var pub rsa.PublicKey
- var ret *rsa.PublicKey
- block, _ := pem.Decode(pubkey)
- _, err := asn1.Unmarshal(block.Bytes, &pub)
- if err != nil {
- return nil, err
- }
- ret = &pub
- return ret, nil
-}
diff --git a/pkg/lib/helpers.go b/pkg/lib/helpers.go
@@ -1,172 +0,0 @@
-package lib
-
-// See LICENSE file for copyright and license details.
-
-import (
- "bytes"
- "crypto/rand"
- "log"
- "math/big"
- "net/http"
- "net/url"
- "os/exec"
- "regexp"
- "strings"
- "time"
-
- "golang.org/x/net/proxy"
-)
-
-// ProxyAddr is the address of our Tor SOCKS port.
-const ProxyAddr = "127.0.0.1:9050"
-
-// CheckError is a handler for errors. It takes an error type as an argument,
-// and issues a log.Fatalln, printing the error and exiting with os.Exit(1).
-func CheckError(err error) {
- if err != nil {
- log.Fatalln(err)
- }
-}
-
-// FetchHSPubkey fetches a hidden service's RSA pubkey by running an external
-// program, giving it an onion address. It returns the retrieved public key as a
-// string.
-func FetchHSPubkey(addr string) string {
- var outb, errb bytes.Buffer
-
- log.Println("Fetching pubkey for:", addr)
-
- cmd := exec.Command("damauth.py", addr)
- cmd.Stdout = &outb
- cmd.Stderr = &errb
- err := cmd.Start()
- CheckError(err)
-
- err = cmd.Wait()
- if err != nil {
- log.Println("Could not fetch descriptor:", err)
- return ""
- }
-
- return outb.String()
-}
-
-// ValidateReq validates our given request against the logic we are checking.
-// The function takes a request data map, and a public key in the form of a
-// string. If the public key is an empty string, the function will run an
-// external program to fetch the node's public key from a Tor HSDir.
-//
-// ValidateReq will first validate "nodetype", looking whether the announcer
-// is a node or a directory.
-// Then, it will validate the onion address using a regular expression.
-// Now, if pubkey is empty, it will run the external program to fetch it. If a
-// descriptor can't be retrieved, it will retry for 10 times, and fail if those
-// are not successful.
-//
-// Continuing, ValidateReq will verify the RSA signature posted by the
-// announcer.
-// If any of the above are invalid, the function will return nil and false.
-// Otherwise, it will return the pubkey as a slice of bytes, and true.
-func ValidateReq(req map[string]string, pubkey string) ([]byte, bool) {
- // Validate nodetype.
- if req["nodetype"] != "node" {
- return nil, false
- }
- // Validate address.
- re, err := regexp.Compile("^[a-z2-7]{16}\\.onion$")
- CheckError(err)
- if len(re.FindString(req["address"])) != 22 {
- return nil, false
- }
- log.Println(req["address"], "seems valid")
-
- if len(pubkey) == 0 {
- // Address is valid, we try to fetch its pubkey from a HSDir
- cnt := 0
- for { // We try until we have it.
- cnt++
- if cnt > 10 {
- // We probably can't get a good HSDir. The client shall retry
- // later on.
- return []byte("Couldn't get a descriptor. Try later."), false
- }
- pubkey = FetchHSPubkey(req["address"])
- if strings.HasPrefix(pubkey, "-----BEGIN RSA PUBLIC KEY-----") &&
- strings.HasSuffix(pubkey, "-----END RSA PUBLIC KEY-----") {
- log.Println("Got descriptor!")
- break
- }
- time.Sleep(2000 * time.Millisecond)
- }
- }
- // Validate signature.
- msg := []byte(req["message"])
- sig := []byte(req["signature"])
- pub, err := ParsePubkeyRsa([]byte(pubkey))
- CheckError(err)
-
- val, err := VerifyMsgRsa(msg, sig, pub)
- CheckError(err)
- if val != true {
- return nil, false
- }
-
- return []byte(pubkey), true
-}
-
-// HTTPPost sends an HTTP POST request to the given host.
-// Takes the host to request and the data to post as arguments.
-// If the host ends with ".onion", it will enable the request to be performed
-// over a SOCKS proxy, defined in ProxyAddr.
-// On success, it will return the http.Response. Otherwise, it returns an error.
-func HTTPPost(host string, data []byte) (*http.Response, error) {
- socksify := false
- parsedHost, err := url.Parse(host)
- if err != nil {
- return nil, err
- }
- hostname := parsedHost.Hostname()
- if strings.HasSuffix(hostname, ".onion") {
- socksify = true
- }
- httpTransp := &http.Transport{}
- httpClient := &http.Client{Transport: httpTransp}
- if socksify {
- log.Println("Detected a .onion request. Using SOCKS proxy.")
- dialer, err := proxy.SOCKS5("tcp", ProxyAddr, nil, proxy.Direct)
- if err != nil {
- return nil, err
- }
- httpTransp.Dial = dialer.Dial
- }
- request, err := http.NewRequest("POST", host, bytes.NewBuffer(data))
- if err != nil {
- return nil, err
- }
- request.Header.Set("Content-Type", "application/json")
-
- resp, err := httpClient.Do(request)
- if err != nil {
- return nil, err
- }
-
- return resp, nil
-}
-
-// GenRandomASCII returns a random ASCII string of a given length.
-func GenRandomASCII(length int) (string, error) {
- var res string
- for {
- if len(res) >= length {
- return res, nil
- }
- num, err := rand.Int(rand.Reader, big.NewInt(int64(127)))
- if err != nil {
- return "", err
- }
- n := num.Int64()
- if n > 32 && n < 127 {
- res += string(n)
- }
- }
-}