1/*	$OpenBSD: net_utils.c,v 1.6 2020/12/30 18:52:06 benno Exp $ */
2/*-
3 * Copyright (c) 2009 Internet Initiative Japan Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27/* $Id: net_utils.c,v 1.6 2020/12/30 18:52:06 benno Exp $ */
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <net/if.h>
32#include <ifaddrs.h>
33#include <netdb.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include "net_utils.h"
38
39/** Get an interface name from sockaddr */
40const char *
41get_ifname_by_sockaddr(struct sockaddr *sa, char *ifname)
42{
43	struct ifaddrs *addr, *addr0;
44	struct in_addr *in4a, *in4b;
45	const char *ifname0 = NULL;
46	struct in6_addr *in6a, *in6b;
47
48	ifname0 = NULL;
49	/* I want other way than linear search */
50	getifaddrs(&addr0);
51	for (addr = addr0; ifname0 == NULL && addr != NULL;
52	    addr = addr->ifa_next) {
53		if (addr->ifa_addr == NULL ||
54		    addr->ifa_addr->sa_family != sa->sa_family ||
55		    addr->ifa_addr->sa_len != sa->sa_len)
56			continue;
57		switch (addr->ifa_addr->sa_family) {
58		default:
59			continue;
60		case AF_INET:
61			in4a = &((struct sockaddr_in *)addr->ifa_addr)
62			    ->sin_addr;
63			in4b = &((struct sockaddr_in *)sa)->sin_addr;
64			if (in4a->s_addr == in4b->s_addr) {
65				strlcpy(ifname, addr->ifa_name, IF_NAMESIZE);
66				ifname0 = ifname;
67			}
68			break;
69		case AF_INET6:
70			in6a = &((struct sockaddr_in6 *)addr->ifa_addr)
71			    ->sin6_addr;
72			in6b = &((struct sockaddr_in6 *)sa)->sin6_addr;
73			if (IN6_ARE_ADDR_EQUAL(in6a, in6b)) {
74				strlcpy(ifname, addr->ifa_name, IF_NAMESIZE);
75				ifname0 = ifname;
76			}
77			break;
78		}
79	}
80	freeifaddrs(addr0);
81
82	return ifname0;
83}
84
85/**
86 * Convert argument like "192.168.160.1:1723/tcp" or "[::1]:1723/tcp" to
87 * match getaddrinfo(3)'s specification and pass them to getaddrinfo(3).
88 */
89int
90addrport_parse(const char *addrport, int proto, struct addrinfo **p_ai)
91{
92	char buf[256];
93	char *servp, *nodep, *slash;
94	struct addrinfo hints;
95
96	strlcpy(buf, addrport, sizeof(buf));
97	if (buf[0] == '[' && (servp = strchr(buf, ']')) != NULL) {
98		nodep = buf + 1;
99		*servp++ = '\0';
100		if (*servp != ':')
101			servp = NULL;
102	} else {
103		nodep = buf;
104		servp = strrchr(nodep, ':');
105	}
106	if (servp != NULL) {
107		*servp = '\0';
108		servp++;
109		slash = strrchr(servp, '/');
110		if (slash != NULL) {
111			/*
112			 * Ignore like "/tcp"
113			 */
114			*slash = '\0';
115			slash++;
116		}
117	} else
118		servp = NULL;
119	memset(&hints, 0, sizeof(hints));
120	hints.ai_flags = AI_NUMERICHOST;
121	hints.ai_family = AF_UNSPEC;
122	switch (proto) {
123	case IPPROTO_TCP:
124		hints.ai_socktype = SOCK_STREAM;
125		break;
126	case IPPROTO_UDP:
127		hints.ai_socktype = SOCK_DGRAM;
128		break;
129	}
130	hints.ai_protocol = proto;
131
132	return getaddrinfo(nodep, servp, &hints, p_ai);
133}
134
135/**
136 * Make a string like "192.168.160.1:1723" or "[::1]:1723" from a struct
137 * sockaddr
138 *
139 * @param	buf	the buffer to be stored a string
140 * @param	lbuf	the length of the buf
141 */
142const char *
143addrport_tostring(struct sockaddr *sa, socklen_t salen, char *buf, int lbuf)
144{
145	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
146
147	if (getnameinfo(sa, salen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
148	    NI_NUMERICHOST | NI_NUMERICSERV) != 0)
149		return NULL;
150
151	switch (sa->sa_family) {
152	case AF_INET6:
153		strlcpy(buf, "[", lbuf);
154		strlcat(buf, hbuf, lbuf);
155		strlcat(buf, "]:", lbuf);
156		strlcat(buf, sbuf, lbuf);
157		break;
158	case AF_INET:
159		strlcpy(buf, hbuf, lbuf);
160		strlcat(buf, ":", lbuf);
161		strlcat(buf, sbuf, lbuf);
162		break;
163	default:
164		return NULL;
165	}
166
167	return buf;
168}
169
170/** Convert 32bit IPv4 netmask to the prefix length in host byte order */
171int
172netmask2prefixlen(uint32_t mask)
173{
174    switch(mask) {
175    case 0x00000000:  return  0;
176    case 0x80000000:  return  1;
177    case 0xC0000000:  return  2;
178    case 0xE0000000:  return  3;
179    case 0xF0000000:  return  4;
180    case 0xF8000000:  return  5;
181    case 0xFC000000:  return  6;
182    case 0xFE000000:  return  7;
183    case 0xFF000000:  return  8;
184    case 0xFF800000:  return  9;
185    case 0xFFC00000:  return 10;
186    case 0xFFE00000:  return 11;
187    case 0xFFF00000:  return 12;
188    case 0xFFF80000:  return 13;
189    case 0xFFFC0000:  return 14;
190    case 0xFFFE0000:  return 15;
191    case 0xFFFF0000:  return 16;
192    case 0xFFFF8000:  return 17;
193    case 0xFFFFC000:  return 18;
194    case 0xFFFFE000:  return 19;
195    case 0xFFFFF000:  return 20;
196    case 0xFFFFF800:  return 21;
197    case 0xFFFFFC00:  return 22;
198    case 0xFFFFFE00:  return 23;
199    case 0xFFFFFF00:  return 24;
200    case 0xFFFFFF80:  return 25;
201    case 0xFFFFFFC0:  return 26;
202    case 0xFFFFFFE0:  return 27;
203    case 0xFFFFFFF0:  return 28;
204    case 0xFFFFFFF8:  return 29;
205    case 0xFFFFFFFC:  return 30;
206    case 0xFFFFFFFE:  return 31;
207    case 0xFFFFFFFF:  return 32;
208    }
209    return -1;
210}
211