github.com/aldelo/common@v1.5.1/helper-net.go (about)

     1  package helper
     2  
     3  /*
     4   * Copyright 2020-2023 Aldelo, LP
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"github.com/aldelo/common/rest"
    24  	"io/ioutil"
    25  	"net"
    26  	"net/http"
    27  	"net/url"
    28  	"strings"
    29  	"time"
    30  )
    31  
    32  // GetNetListener triggers the specified port to listen via tcp
    33  func GetNetListener(port uint) (net.Listener, error) {
    34  	if l, e := net.Listen("tcp", fmt.Sprintf(":%d", port)); e != nil {
    35  		return nil, fmt.Errorf("Listen Tcp on Port %d Failed: %v", port, e)
    36  	} else {
    37  		return l, nil
    38  	}
    39  }
    40  
    41  // IsHttpsEndpoint returns true if url is https, false if otherwise
    42  func IsHttpsEndpoint(url string) bool {
    43  	return strings.ToLower(Left(url, 8)) == "https://"
    44  }
    45  
    46  // GetLocalIP returns the first non loopback ip
    47  func GetLocalIP() string {
    48  	if addrs, err := net.InterfaceAddrs(); err != nil {
    49  		return ""
    50  	} else {
    51  		for _, a := range addrs {
    52  			if ip, ok := a.(*net.IPNet); ok && !ip.IP.IsLoopback() && !ip.IP.IsInterfaceLocalMulticast() && !ip.IP.IsLinkLocalMulticast() && !ip.IP.IsLinkLocalUnicast() && !ip.IP.IsMulticast() && !ip.IP.IsUnspecified() {
    53  				if ip.IP.To4() != nil {
    54  					return ip.IP.String()
    55  				}
    56  			}
    57  		}
    58  
    59  		return ""
    60  	}
    61  }
    62  
    63  // DnsLookupIps returns list of IPs for the given host
    64  // if host is private on aws route 53, then lookup ip will work only when within given aws vpc that host was registered with
    65  func DnsLookupIps(host string) (ipList []net.IP) {
    66  	if ips, err := net.LookupIP(host); err != nil {
    67  		return []net.IP{}
    68  	} else {
    69  		for _, ip := range ips {
    70  			ipList = append(ipList, ip)
    71  		}
    72  		return ipList
    73  	}
    74  }
    75  
    76  // DnsLookupSrvs returns list of IP and port addresses based on host
    77  // if host is private on aws route 53, then lookup ip will work only when within given aws vpc that host was registered with
    78  func DnsLookupSrvs(host string) (ipList []string) {
    79  	if _, addrs, err := net.LookupSRV("", "", host); err != nil {
    80  		return []string{}
    81  	} else {
    82  		for _, v := range addrs {
    83  			ipList = append(ipList, fmt.Sprintf("%s:%d", v.Target, v.Port))
    84  		}
    85  
    86  		return ipList
    87  	}
    88  }
    89  
    90  // ParseHostFromURL will parse out the host name from url
    91  func ParseHostFromURL(url string) string {
    92  	parts := strings.Split(strings.ReplaceAll(strings.ReplaceAll(strings.ToLower(url), "https://", ""), "http://", ""), "/")
    93  
    94  	if len(parts) >= 0 {
    95  		return parts[0]
    96  	} else {
    97  		return ""
    98  	}
    99  }
   100  
   101  // VerifyGoogleReCAPTCHAv2 will verify recaptcha v2 response data against given secret and obtain a response from google server
   102  func VerifyGoogleReCAPTCHAv2(response string, secret string) (success bool, challengeTs time.Time, hostName string, err error) {
   103  	if LenTrim(response) == 0 {
   104  		return false, time.Time{}, "", fmt.Errorf("ReCAPTCHA Response From CLient is Required")
   105  	}
   106  
   107  	if LenTrim(secret) == 0 {
   108  		return false, time.Time{}, "", fmt.Errorf("ReCAPTCHA Secret Key is Required")
   109  	}
   110  
   111  	u := fmt.Sprintf("https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s", url.PathEscape(secret), url.PathEscape(response))
   112  
   113  	if statusCode, responseBody, e := rest.POST(u, []*rest.HeaderKeyValue{}, ""); e != nil {
   114  		return false, time.Time{}, "", fmt.Errorf("ReCAPTCHA Service Failed: %s", e)
   115  	} else {
   116  		if statusCode != 200 {
   117  			return false, time.Time{}, "", fmt.Errorf("ReCAPTCHA Service Failed: Status Code %d", statusCode)
   118  		} else {
   119  			m := make(map[string]json.RawMessage)
   120  			if err = json.Unmarshal([]byte(responseBody), &m); err != nil {
   121  				return false, time.Time{}, "", fmt.Errorf("ReCAPTCHA Service Response Failed: (Parse Json Response Error) %s", err)
   122  			} else {
   123  				if m == nil {
   124  					return false, time.Time{}, "", fmt.Errorf("ReCAPTCHA Service Response Failed: %s", "Json Response Map Nil")
   125  				} else {
   126  					// response json from google is valid
   127  					if strings.ToLower(string(m["success"])) == "true" {
   128  						success = true
   129  					}
   130  
   131  					challengeTs = ParseDateTime(string(m["challenge_ts"]))
   132  					hostName = string(m["hostname"])
   133  
   134  					if !success {
   135  						errs := string(m["error-codes"])
   136  						s := []string{}
   137  
   138  						if err = json.Unmarshal([]byte(errs), &s); err != nil {
   139  							err = fmt.Errorf("Parse ReCAPTCHA Verify Errors Failed: %s", err)
   140  						} else {
   141  							buf := ""
   142  
   143  							for _, v := range s {
   144  								if LenTrim(v) > 0 {
   145  									if LenTrim(buf) > 0 {
   146  										buf += ", "
   147  									}
   148  
   149  									buf += v
   150  								}
   151  							}
   152  
   153  							err = fmt.Errorf("ReCAPTCHA Verify Errors: %s", buf)
   154  						}
   155  					}
   156  
   157  					return success, challengeTs, hostName, err
   158  				}
   159  			}
   160  		}
   161  	}
   162  }
   163  
   164  // ReadHttpRequestBody reads raw body from http request body object,
   165  // and then sets the read body back to the request (once reading will remove the body content if not restored)
   166  func ReadHttpRequestBody(req *http.Request) ([]byte, error) {
   167  	if req == nil {
   168  		return []byte{}, fmt.Errorf("Http Request Nil")
   169  	}
   170  
   171  	if body, err := ioutil.ReadAll(req.Body); err != nil {
   172  		return []byte{}, err
   173  	} else {
   174  		req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
   175  		return body, nil
   176  	}
   177  }
   178  
   179  // ReadHttpRequestHeaders parses headers into map
   180  func ReadHttpRequestHeaders(req *http.Request) (map[string]string, error) {
   181  	if req == nil {
   182  		return nil, fmt.Errorf("Http Request Nil")
   183  	}
   184  
   185  	m := make(map[string]string)
   186  
   187  	for k, v := range req.Header {
   188  		buf := ""
   189  
   190  		for _, d := range v {
   191  			if LenTrim(buf) > 0 {
   192  				buf += "; "
   193  			}
   194  
   195  			buf += d
   196  		}
   197  
   198  		m[k] = buf
   199  	}
   200  
   201  	return m, nil
   202  }
   203  
   204  // ParseHttpHeader parses headers into map[string]string,
   205  // where slice of header values for a key is delimited with semi-colon (;)
   206  func ParseHttpHeader(respHeader http.Header) (map[string]string, error) {
   207  	if respHeader == nil {
   208  		return nil, fmt.Errorf("Http Response Header Nil")
   209  	}
   210  
   211  	m := make(map[string]string)
   212  
   213  	for k, v := range respHeader {
   214  		buf := ""
   215  
   216  		for _, d := range v {
   217  			if LenTrim(buf) > 0 {
   218  				buf += "; "
   219  			}
   220  
   221  			buf += d
   222  		}
   223  
   224  		m[k] = buf
   225  	}
   226  
   227  	return m, nil
   228  }
   229  
   230  // EncodeHttpHeaderMapToString converts header map[string]string to string representation
   231  func EncodeHttpHeaderMapToString(headerMap map[string]string) string {
   232  	if headerMap == nil {
   233  		return ""
   234  	} else {
   235  		buf := ""
   236  
   237  		for k, v := range headerMap {
   238  			buf += fmt.Sprintf("%s: %s\n", k, v)
   239  		}
   240  
   241  		return buf
   242  	}
   243  }