···11+# Changesets
22+33+Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
44+with multi-package repos, or single-package repos to help you version and publish your code. You can
55+find the full documentation for it [in our repository](https://github.com/changesets/changesets)
66+77+We have a quick list of common questions to get you started engaging with this project in
88+[our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md)
···11+MIT License
22+33+Copyright (c) Phil Pluckthun,
44+Copyright (c) 2016 - 2020 Node Fetch Team,
55+Copyright (c) Remix Software Inc. 2020-2021,
66+Copyright (c) Shopify Inc. 2022-2024
77+88+Permission is hereby granted, free of charge, to any person obtaining a copy
99+of this software and associated documentation files (the "Software"), to deal
1010+in the Software without restriction, including without limitation the rights
1111+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1212+copies of the Software, and to permit persons to whom the Software is
1313+furnished to do so, subject to the following conditions:
1414+1515+The above copyright notice and this permission notice shall be included in all
1616+copies or substantial portions of the Software.
1717+1818+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1919+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2020+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2121+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2222+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2323+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2424+SOFTWARE.
+22
README.md
···11+# lan-network
22+33+**Best-effort discovery of the machine's default gateway and local network IPv4 address exclusively with UDP sockets.**
44+55+This utility attempts to determine the interface and IPv4 address of a machine
66+on the local network. It'll attempt to determine the default gateway and
77+return the corresponding network interface assignment, both when the network
88+is online and offline.
99+1010+The LAN Network it attempts to pick is the one that the machine uses to connect
1111+to the internet. Determining it is useful to pick the machine's IP address that
1212+is generally used to connect to it from other devices on the network.
1313+1414+`lanNetwork()` makes three separate attempts to guess the local network:
1515+1616+1. Create a socket to a publicly routed IP, and return the assignment matching the socket's local address
1717+2. Broadcast DHCP discovery packets on all routable network assignments and listen for replies
1818+3. Highest priority assignment
1919+2020+`lanNetworkSync()` does the same synchronously by spawning a child process
2121+and blocking until a result is determined. Using this method is generally
2222+not recommended.
···11+import { spawnSync } from 'child_process';
22+import { dhcpDiscover } from './dhcp';
33+import { probeDefaultRoute } from './route';
44+import { interfaceAssignments, matchAssignment } from './network';
55+import type { GatewayAssignment } from './types';
66+77+const DEFAULT_ASSIGNMENT: GatewayAssignment = {
88+ iname: 'lo0',
99+ address: '127.0.0.1',
1010+ netmask: '255.0.0.0',
1111+ family: 'IPv4',
1212+ mac: '00:00:00:00:00:00',
1313+ internal: true,
1414+ cidr: '127.0.0.1/8',
1515+ gateway: null,
1616+};
1717+1818+export async function lanNetwork(): Promise<GatewayAssignment> {
1919+ // Get IPv4 network assignments, sorted by:
2020+ // - external first
2121+ // - LAN-reserved IP range priority
2222+ // - address value
2323+ const assignments = interfaceAssignments();
2424+ if (!assignments.length) {
2525+ // If we have no assignments (which shouldn't ever happen, we make up a loopback interface)
2626+ return DEFAULT_ASSIGNMENT;
2727+ }
2828+2929+ let assignment: GatewayAssignment | null;
3030+3131+ // First, we attempt to probe the default route to a publicly routed IP
3232+ // This will generally fail if there's no route, e.g. if the network is offline
3333+ try {
3434+ const defaultRoute = await probeDefaultRoute();
3535+ // If this route matches a known assignment, return it without a gateway
3636+ if ((assignment = matchAssignment(assignments, defaultRoute))) {
3737+ return assignment;
3838+ }
3939+ } catch {
4040+ // Ignore errors, since we have a fallback method
4141+ }
4242+4343+ // Second, attempt to discover a gateway's DHCP network
4444+ // Because without a gateway we won't get a reply, we do this in parallel
4545+ const discoveries = await Promise.allSettled(
4646+ assignments.map(assignment => {
4747+ // For each assignment, we send a DHCPDISCOVER packet to its network mask
4848+ return dhcpDiscover(assignment);
4949+ })
5050+ );
5151+ for (const discovery of discoveries) {
5252+ // The first discovered gateway is returned, if it matches an assignment
5353+ if (discovery.status === 'fulfilled' && discovery.value) {
5454+ const dhcpRoute = discovery.value;
5555+ if ((assignment = matchAssignment(assignments, dhcpRoute))) {
5656+ return assignment;
5757+ }
5858+ }
5959+ }
6060+6161+ // As a fallback, we choose the first assignment, since they're ordered by likely candidates
6262+ // This may return 127.0.0.1, typically as a last resort
6363+ return { ...assignments[0], gateway: null };
6464+}
6565+6666+export function lanNetworkSync(): GatewayAssignment {
6767+ const subprocessPath = require.resolve('lan-network/subprocess');
6868+ const { error, status, stdout } = spawnSync(
6969+ process.execPath,
7070+ [subprocessPath],
7171+ {
7272+ shell: false,
7373+ timeout: 500,
7474+ encoding: 'utf8',
7575+ windowsVerbatimArguments: false,
7676+ windowsHide: true,
7777+ }
7878+ );
7979+ if (status || error) {
8080+ return DEFAULT_ASSIGNMENT;
8181+ } else if (!status && typeof stdout === 'string') {
8282+ const json = JSON.parse(stdout.trim()) as GatewayAssignment;
8383+ return typeof json === 'object' && json && 'address' in json
8484+ ? json
8585+ : DEFAULT_ASSIGNMENT;
8686+ } else {
8787+ return DEFAULT_ASSIGNMENT;
8888+ }
8989+}