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 */
40 const char *
get_ifname_by_sockaddr(struct sockaddr * sa,char * ifname)41 get_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 */
89 int
addrport_parse(const char * addrport,int proto,struct addrinfo ** p_ai)90 addrport_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 */
142 const char *
addrport_tostring(struct sockaddr * sa,socklen_t salen,char * buf,int lbuf)143 addrport_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 */
171 int
netmask2prefixlen(uint32_t mask)172 netmask2prefixlen(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