1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * Network interfaces.
7  *
8  * Copyright: Eugene Wissner 2018-2020.
9  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
10  *                  Mozilla Public License, v. 2.0).
11  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
12  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/iface.d,
13  *                 tanya/net/iface.d)
14  */
15 module tanya.net.iface;
16 
17 import tanya.algorithm.mutation;
18 import tanya.container.string;
19 import tanya.meta.trait;
20 import tanya.meta.transform;
21 import tanya.range;
22 
23 version (Windows)
24 {
25     private union NET_LUID_LH { ulong Value, Info; }
26     private alias NET_LUID = NET_LUID_LH;
27     private alias NET_IFINDEX = uint;
28     private enum IF_MAX_STRING_SIZE = 256;
29     extern(Windows) @nogc nothrow private @system
30     {
31         uint ConvertInterfaceNameToLuidA(const(char)* InterfaceName,
32             NET_LUID* InterfaceLuid);
33         uint ConvertInterfaceLuidToIndex(const(NET_LUID)* InterfaceLuid,
34             NET_IFINDEX* InterfaceIndex);
35         uint ConvertInterfaceIndexToLuid(NET_IFINDEX InterfaceIndex,
36             NET_LUID* InterfaceLuid);
37         uint ConvertInterfaceLuidToNameA(const(NET_LUID)* InterfaceLuid,
38             char* InterfaceName,
39             size_t Length);
40     }
41 }
42 else version (Posix)
43 {
44     import core.sys.posix.net.if_;
45 }
46 
47 /**
48  * Converts the name of a network interface to its index.
49  *
50  * If an interface with the name $(D_PARAM name) cannot be found or another
51  * error occurres, returns 0.
52  *
53  * Params:
54  *  name = Interface name.
55  *
56  * Returns: Returns interface index or 0.
57  */
58 uint nameToIndex(R)(R name) @trusted
59 if (isInputRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
60 {
61     version (Windows)
62     {
63         if (name.length > IF_MAX_STRING_SIZE)
64         {
65             return 0;
66         }
67         char[IF_MAX_STRING_SIZE + 1] buffer;
68         NET_LUID luid;
69 
70         copy(name, buffer[]);
71         buffer[name.length] = '\0';
72 
73         if (ConvertInterfaceNameToLuidA(buffer.ptr, &luid) != 0)
74         {
75             return 0;
76         }
77         NET_IFINDEX index;
78         if (ConvertInterfaceLuidToIndex(&luid, &index) == 0)
79         {
80             return index;
81         }
82         return 0;
83     }
84     else version (Posix)
85     {
86         if (name.length >= IF_NAMESIZE)
87         {
88             return 0;
89         }
90         char[IF_NAMESIZE] buffer;
91 
92         copy(name, buffer[]);
93         buffer[name.length] = '\0';
94 
95         return if_nametoindex(buffer.ptr);
96     }
97 }
98 
99 ///
100 @nogc nothrow @safe unittest
101 {
102     version (linux)
103     {
104         assert(nameToIndex("lo") == 1);
105     }
106     else version (Windows)
107     {
108         assert(nameToIndex("loopback_0") == 1);
109     }
110     else
111     {
112         assert(nameToIndex("lo0") == 1);
113     }
114     assert(nameToIndex("ecafretni") == 0);
115 }
116 
117 /**
118  * Converts the index of a network interface to its name.
119  *
120  * If an interface with the $(D_PARAM index) cannot be found or another
121  * error occurres, returns an empty $(D_PSYMBOL String).
122  *
123  * Params:
124  *  index = Interface index.
125  *
126  * Returns: Returns interface name or an empty $(D_PSYMBOL String).
127  */
128 String indexToName(uint index) @nogc nothrow @trusted
129 {
130     import tanya.memory.op : findNullTerminated;
131 
132     version (Windows)
133     {
134         NET_LUID luid;
135         if (ConvertInterfaceIndexToLuid(index, &luid) != 0)
136         {
137             return String();
138         }
139 
140         char[IF_MAX_STRING_SIZE + 1] buffer;
141         if (ConvertInterfaceLuidToNameA(&luid,
142                                         buffer.ptr,
143                                         IF_MAX_STRING_SIZE + 1) != 0)
144         {
145             return String();
146         }
147         return String(findNullTerminated(buffer));
148     }
149     else version (Posix)
150     {
151         char[IF_NAMESIZE] buffer;
152         if (if_indextoname(index, buffer.ptr) is null)
153         {
154             return String();
155         }
156         return String(findNullTerminated(buffer));
157     }
158 }
159 
160 /**
161  * $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects
162  * the protocol family which will be used for communication.
163  */
164 enum AddressFamily : int
165 {
166     unspec    = 0,     /// Unspecified.
167     local     = 1,     /// Local to host (pipes and file-domain).
168     unix      = local, /// POSIX name for PF_LOCAL.
169     inet      = 2,     /// IP protocol family.
170     ax25      = 3,     /// Amateur Radio AX.25.
171     ipx       = 4,     /// Novell Internet Protocol.
172     appletalk = 5,     /// Appletalk DDP.
173     netrom    = 6,     /// Amateur radio NetROM.
174     bridge    = 7,     /// Multiprotocol bridge.
175     atmpvc    = 8,     /// ATM PVCs.
176     x25       = 9,     /// Reserved for X.25 project.
177     inet6     = 10,    /// IP version 6.
178 }