aboutsummaryrefslogtreecommitdiff
path: root/core/net/interface_darwin.odin
blob: e2a9a73ca9dcb296dee1238d53d26124cb059635 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package net
//+build darwin

/*
	Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
	For other protocols and their features, see subdirectories of this package.
*/

/*
	Copyright 2022 Tetralux        <tetraluxonpc@gmail.com>
	Copyright 2022 Colin Davidson  <colrdavidson@gmail.com>
	Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
	Made available under Odin's BSD-3 license.

	List of contributors:
		Tetralux:        Initial implementation
		Colin Davidson:  Linux platform code, OSX platform code, Odin-native DNS resolver
		Jeroen van Rijn: Cross platform unification, code style, documentation

*/

import "core:os"
import "core:strings"

@(private)
_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
	context.allocator = allocator

	head: ^os.ifaddrs

	if res := os._getifaddrs(&head); res < 0 {
		return {}, .Unable_To_Enumerate_Network_Interfaces
	}

	/*
		Unlike Windows, *nix regrettably doesn't return all it knows about an interface in one big struct.
		We're going to have to iterate over a list and coalesce information as we go.
	*/
	ifaces: map[string]^Network_Interface
	defer delete(ifaces)

	for ifaddr := head; ifaddr != nil; ifaddr = ifaddr.next {
		adapter_name := string(ifaddr.name)

		/*
			Check if we have seen this interface name before so we can reuse the `Network_Interface`.
			Else, create a new one.
		*/
		if adapter_name not_in ifaces {
			ifaces[adapter_name] = new(Network_Interface)
			ifaces[adapter_name].adapter_name = strings.clone(adapter_name)
		}
		iface := ifaces[adapter_name]

		address: Address
		netmask: Netmask

		if ifaddr.address != nil {
			switch int(ifaddr.address.family) {
			case os.AF_INET, os.AF_INET6:
				address = _sockaddr_basic_to_endpoint(ifaddr.address).address
			}
		}

		if ifaddr.netmask != nil {
			switch int(ifaddr.netmask.family) {
			case os.AF_INET, os.AF_INET6:
				netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
			}
		}

		if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
			switch int(ifaddr.broadcast_or_dest.family) {
			case os.AF_INET, os.AF_INET6:
				broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
				append(&iface.multicast, broadcast)
			}
		}

		if address != nil {
			lease := Lease{
				address = address,
				netmask = netmask,
			}
			append(&iface.unicast, lease)
		}

		/*
			TODO: Refine this based on the type of adapter.
		*/
		state := Link_State{}

		if .UP in ifaddr.flags {
			state += {.Up}
		}

		/*if .DORMANT in ifaddr.flags {
			state |= {.Dormant}
		}*/

		if .LOOPBACK in ifaddr.flags {
			state += {.Loopback}
		}
		iface.link.state = state
	}

	/*
		Free the OS structures.
	*/
	os._freeifaddrs(head)

	/*
		Turn the map into a slice to return.
	*/
	_interfaces := make([dynamic]Network_Interface, 0, allocator)
	for _, iface in ifaces {
		append(&_interfaces, iface^)
		free(iface)
	}
	return _interfaces[:], {}
}