How to retrieve address of current machine? - go

The following grabs the local IP addresses:
package main
import (
"fmt"
"net"
)
func main() {
a, _ := net.LookupHost("localhost")
fmt.Printf("Addresses: %#+v\n",a)
}
Is this how you would normally get the local IP address, filtering the slice manually according to need?

Here's a quick and dirty modification of a code snippet originally posted by Russ Cox to the golang-nuts google group:
package main
import (
"fmt"
"net"
"os"
)
func main() {
tt, err := net.Interfaces()
if err != nil {
panic(err)
}
for _, t := range tt {
aa, err := t.Addrs()
if err != nil {
panic(err)
}
for _, a := range aa {
ipnet, ok := a.(*net.IPNet)
if !ok {
continue
}
v4 := ipnet.IP.To4()
if v4 == nil || v4[0] == 127 { // loopback address
continue
}
fmt.Printf("%v\n", v4)
}
os.Exit(0)
}
os.Exit(1)
}

Finding the correct IP address can be a problem because a typical server and development machine may have multiple interfaces. For example $ifconfig on my Mac returns the following interfaces lo0, gif0, stf0, en0, en1, en2, bridge0, p2p0, vmnet1, vmnet8, tap0, fw0, en4
Basically, you need to know your environment.
It's not pretty, but for what it's worth, this is what I use on a production Ubuntu server. It also works on my development Mac 10.9.2, who know what it does on Windows.
package main
import (
"net"
"strings"
)
func findIPAddress() string {
if interfaces, err := net.Interfaces(); err == nil {
for _, interfac := range interfaces {
if interfac.HardwareAddr.String() != "" {
if strings.Index(interfac.Name, "en") == 0 ||
strings.Index(interfac.Name, "eth") == 0 {
if addrs, err := interfac.Addrs(); err == nil {
for _, addr := range addrs {
if addr.Network() == "ip+net" {
pr := strings.Split(addr.String(), "/")
if len(pr) == 2 && len(strings.Split(pr[0], ".")) == 4 {
return pr[0]
}
}
}
}
}
}
}
}
return ""
}
func main() {
println(findIPAddress())
}

I have one addition: The current solutions shown above are not working at least on FreeBSD 10 because the system returns the addresses as CIDR notation e.g. 192.168.1.2/32! Therefore, it is necessary to change the solution a little bit:
package main
import (
"fmt"
"net"
"os"
"strings"
)
func main() {
addrs, err := net.InterfaceAddrs()
if err != nil {
fmt.Println("Error: " + err.Error())
os.Exit(1)
}
for _, a := range addrs {
text := a.String()
if strings.Contains(text, `/`) {
text = text[:strings.Index(text, `/`)]
}
ip := net.ParseIP(text)
if !ip.IsLoopback() && !ip.IsUnspecified() {
fmt.Println(ip)
}
}
}
The part ...
if strings.Contains(text, `/`) {
text = text[:strings.Index(text, `/`)]
}
... detects if / is part of the address and delete this part!
Best regards,
Thorsten

These slight modifications worked for me:
package main
import (
"fmt"
"net"
"os"
)
func myip() {
os.Stdout.WriteString("myip:\n")
addrs, err := net.InterfaceAddrs()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
return
}
for _, a := range addrs {
ip := net.ParseIP(a.String())
fmt.Printf("addr: %v loopback=%v\n", a, ip.IsLoopback())
}
fmt.Println()
}
func myip2() {
os.Stdout.WriteString("myip2:\n")
tt, err := net.Interfaces()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
return
}
for _, t := range tt {
aa, err := t.Addrs()
if err != nil {
fmt.Errorf("error: %v\n", err.Error())
continue
}
for _, a := range aa {
ip := net.ParseIP(a.String())
fmt.Printf("%v addr: %v loopback=%v\n", t.Name, a, ip.IsLoopback())
}
}
fmt.Println()
}
func main() {
fmt.Println("myip -- begin")
myip()
myip2()
fmt.Println("myip -- end")
}

Related

Iterating over a multiline variable to return all ips

I'm trying to build a simple port scanner for a beginner golang project
and most of the code works as intended, but I'm having a problem with
ipv4_gen() function to return all ips that are generated line by line
and pass them to another function to scan them currently ipv4_gen()
Returns the first line only is there a way I can iterate over the ip variable to returns all the ips line by line?
package main
import (
"fmt"
"log"
"net"
"strconv"
"time"
)
func ipv4_gen() string {
ip, ipnet, err := net.ParseCIDR("192.168.1.1/24")
if err != nil {
log.Fatal(err)
}
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
return ip.String()
}
return ip.String()
}
func inc(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}
func port_scanner(host string) {
port := strconv.Itoa(80)
conn, err := net.DialTimeout("tcp", host+":"+port, 1*time.Second)
if err == nil {
fmt.Println("Host:", conn.RemoteAddr().String(), "open")
conn.Close()
}
}
func main() {
port_scanner(ipv4_gen())
}
Here's the link if you wanna run the code https://play.golang.org/p/YWvgnowZzhI
To return multiple results from a function (especially when generating potentially thousands of results) it's idiomatic in Go to use a channel.
package main
import (
"fmt"
"log"
"net"
"strconv"
"time"
)
func ipv4_gen(out chan string) {
ip, ipnet, err := net.ParseCIDR("192.168.1.1/24")
if err != nil {
log.Fatal(err)
}
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
out <- ip.String()
}
close(out)
}
func inc(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}
func port_scanner(host string) {
port := strconv.Itoa(80)
conn, err := net.DialTimeout("tcp", host+":"+port, 1*time.Second)
if err == nil {
fmt.Println("Host:", conn.RemoteAddr().String(), "open")
conn.Close()
}
}
func main() {
ips := make(chan string)
go ipv4_gen(ips)
for s := range ips {
port_scanner(s)
}
}
While channels are useful for communicating between goroutines, you do not benefit from using goroutines to separate address iteration from scanning; the channel slows things down. A simpler and quicker solution is to use an iterator object. An iterator object in any language is designed to do exactly what you requested, to "iterate over the ip variable to returns all the ips".
Here is the code to do so using the the IPAddress Go library. Disclaimer: I am the project manager.
package main
import (
"fmt"
"github.com/seancfoley/ipaddress-go/ipaddr"
"net"
"time"
)
func ipv4_gen() ipaddr.IPAddressIterator {
block := ipaddr.NewIPAddressString("192.168.1.0/24").GetAddress()
iterator := block.WithoutPrefixLen().Iterator()
iterator.Next() // skip the first address 192.168.1.0
return iterator
}
func port_scanner(iterator ipaddr.IPAddressIterator) {
for iterator.HasNext() {
conn, err := net.DialTimeout("tcp",
fmt.Sprint(iterator.Next(), ":", 80), time.Second)
if err == nil {
fmt.Println("Host:", conn.RemoteAddr().String(), "open")
conn.Close()
}
}
}
func main() {
port_scanner(ipv4_gen())
}
If I understood your question correctly then this should work.
package main
import (
"fmt"
"log"
"net"
"os"
"strconv"
"time"
)
func ipv4_gen(ch chan<- string) {
ip, ipnet, err := net.ParseCIDR("192.168.1.1/24")
if err != nil {
log.Fatal(err)
}
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
ch <- ip.String()
}
close(ch)
}
func inc(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
if ip[j]++; ip[j] > 0 {
break
}
}
}
func port_scanner(hosts <-chan string) {
for host := range hosts {
port := strconv.Itoa(80)
conn, err := net.DialTimeout("tcp", host+":"+port, 1*time.Second)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
fmt.Println("Host:", conn.RemoteAddr().String(), "open")
conn.Close()
}
}
func main() {
ip := make(chan string)
go ipv4_gen(ip)
port_scanner(ip)
}

Go udp connection dial once, and send many

please consider this code below, it is a simplified version of a service. I launch a no. of goroutines as needed during its lifetime, and as they go about doing things, they need to send udp messages to a set destination.
package main
import (
"fmt"
"log"
"net"
"time"
)
const (
udp_dest = "192.168.1.200:514"
)
func main() {
fmt.Println("Hello")
message := "this is a test"
log_message(&message)
go worker(1)
go worker(2)
go worker(3)
go worker(4)
time.Sleep(3009 * time.Second)
}
func log_message(message *string) {
RemoteAddr, err := net.ResolveUDPAddr("udp", udp_dest)
if err != nil {
//fmt.Println("Err, net.ResolveUDPAddr", err)
return
}
conn, err := net.DialUDP("udp", nil, RemoteAddr)
if err != nil {
return
}
udp_message := fmt.Sprintf("<30> %s", *message)
Bytes, _ := conn.Write([]byte(udp_message))
log.Printf("Sent %d Bytes to %s\n", Bytes, udp_dest)
}
func worker(tag int) {
i := 0
for {
worker_message := fmt.Sprintf("Some message from worker%d, loop: %d", tag, i)
log_message(&worker_message)
// do some work..
time.Sleep(300 * time.Second)
i += 1
}
}
In my log_message, everytime it gets called we're calling net.DialUDP which I feel is wasteful. I tried experimenting with global variables &net.UDPConn et al, but could not get to work.
Please show how to achieve/optimize this? There's only one UDP destination, and I'd like the daemon to Dial once at its start, and then just Write as needed.
Thanks!
here's what I got so far:
package main
import (
"fmt"
"log"
"net"
"time"
)
const (
udp_dest = "192.168.1.200:514"
)
var (
myconn *net.UDPConn
)
func main() {
fmt.Println("Hello")
message := "this is a test"
log_message(&message)
go worker(1)
go worker(2)
go worker(3)
go worker(4)
time.Sleep(3009 * time.Second)
}
func log_message(message *string) {
if myconn == nil {
fmt.Println("Setting up myconn!")
RemoteAddr, err := net.ResolveUDPAddr("udp", udp_dest)
if err != nil {
//fmt.Println("Err, net.ResolveUDPAddr", err)
return
}
myconn, err = net.DialUDP("udp", nil, RemoteAddr)
if err != nil {
return
}
}
udp_message := fmt.Sprintf("<30> %s", *message)
Bytes, _ := myconn.Write([]byte(udp_message))
log.Printf("Sent %d Bytes to %s\n", Bytes, udp_dest)
}
func worker(tag int) {
i := 0
for {
worker_message := fmt.Sprintf("Some message from worker%d, loop: %d", tag, i)
log_message(&worker_message)
// do some work..
time.Sleep(10 * time.Second)
i += 1
}
}
You are almost there. Move the setup code to a function and call it before starting the goroutines.
func main() {
if err := setupLog(); err != nil {
log.Fatal(err)
}
fmt.Println("Hello")
... same as before
}
func setupLog() error {
fmt.Println("Setting up myconn!")
RemoteAddr, err := net.ResolveUDPAddr("udp", udp_dest)
if err != nil {
return err
}
myconn, err = net.DialUDP("udp", nil, RemoteAddr)
return err
}
func log_message(message *string) {
udp_message := fmt.Sprintf("<30> %s", *message)
Bytes, _ := myconn.Write([]byte(udp_message))
log.Printf("Sent %d Bytes to %s\n", Bytes, udp_dest)
}
The code in the question does not work because there's a data race on myconn.

Get permanent MAC address

Is there an easy way to get the permanent MAC Address using Go?
package main
import (
"fmt"
"log"
"net"
)
func getMacAddr() ([]string, error) {
ifas, err := net.Interfaces()
if err != nil {
return nil, err
}
var as []string
for _, ifa := range ifas {
a := ifa.HardwareAddr.String()
if a != "" {
as = append(as, a)
}
}
return as, nil
}
func main() {
as, err := getMacAddr()
if err != nil {
log.Fatal(err)
}
for _, a := range as {
fmt.Println(a)
}
}

Golang for loop of certain part of map

Attempting to create a for loop for each part of a map.
map[
asn:AS10
time:1.428790768e+09
ipv4s:[
68.114.75.0/24
216.215.56.0/22
216.215.60.0/22]
ipv6s:[
2607:f3f8::/32
]]
The above is the map, I'd like to try create a for loop for each value in ipv4s.
I've attempted, but I'm clearly not doing it correctly as it's merely based off my php knowledge.:
for json_map["ipv4s"]{
//whatever
}
PHP version if anyone needs an example rather then me attempting to explain:
foreach($obj->ipv4s as $value) {
echo $value; // return an ip
}
Update
package main
import (
"fmt"
"net/http"
"os"
"encoding/json"
)
func main() {
response, err := http.Get("https://www.enjen.net/asn-blocklist/index.php?asn=" + os.Args[1] + "&type=json_split&api=1")
if err != nil {
fmt.Printf("%s", err)
os.Exit(1)
} else {
defer response.Body.Close()
dec := json.NewDecoder(response.Body)
if dec == nil {
panic("Failed to start decoding JSON data")
}
json_map := make(map[string]interface{})
err = dec.Decode(&json_map)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", json_map)
for i := range json_map {
for _, ip := range json_map[i]["ipv4s"] {
fmt.Printf(ip)
}
}
}
}
Effective Go is a good source once you have completed the tutorial for go.
There it is also described how one iterates over a slice:
for key, value := range json_map {
// ...
}
Or if you don't need the key:
for _, value := range json_map {
// ...
}
You might have to nest two loops, if it is a slice of maps:
for i := range json_map {
for _, ip := range json_map[i]["ipv4s"] {
// ...
}
}

Go to check EC2 environment? Or DNS domain name?

I want to see if the program is being run in EC2 or not.
One way is to run hostname -d in EC2 to get the DNS domain name.
How do I get this DNS domain name in Go.
I looked at the net package using http://golang.org/pkg/net/#LookupNS
But I need to pass an argument.
How do I check if it's in EC2 or not?
Thanks
You can see if there is an interface with a specific name with this function:
package main
import (
"log"
"net"
"strings"
)
func trace(fmt string, args ...interface{}) {
log.Printf(fmt, args...)
}
func HasAddrWithName(name string) (bool, error) {
ifaces, err := net.Interfaces()
if err != nil {
return false, err
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
trace("%s", err)
continue
}
for _, addr := range addrs {
ipaddr, _, err := net.ParseCIDR(addr.String())
if err != nil {
trace("%s", err)
continue
}
hosts, err := net.LookupAddr(ipaddr.String())
if err != nil {
trace("%s", err)
continue
}
for idx, h := range hosts {
trace("%d: %s\n", idx, h)
if strings.Contains(h, name) {
return true, nil
}
}
}
}
return false, nil
}
func main() {
hasAddr, err := HasAddrWithName(".ec2.internal")
if err != nil {
log.Fatal(err)
}
if hasAddr {
log.Println("inside ec2")
return
}
log.Println("Not inside ec2")
}
The function will try to find all the interface an resolve the ip to a dns name. if the name contains the specific string returns true.
The right way, IMO, is to try and hit the metadata API at http://169.254.169.254/latest/meta-data from the machine itself. The worrisome part is that you feel the need to know this in code. I am not quite sure what the use case for this is but it seems to me that there ought to be a way for you to know this outside of your code.
Nevertheless:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
)
func main() {
/* if you just need the hostname */
name, _ := os.Hostname()
fmt.Println(name)
/* if you must hit the EC2 metadata API */
client := http.Client{
Timeout: time.Duration(2 * time.Second),
}
resp, err := client.Get("http://169.254.169.254/latest/meta-data/public-hostname")
if err != nil {
fmt.Println("Probably not on EC2")
fmt.Println(err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}

Resources