Teach nfs_compare_sockaddr() to handle NULL arguments.
[nfs-utils.git] / support / include / sockaddr.h
1 /*
2  * Copyright 2009 Oracle.  All rights reserved.
3  *
4  * This file is part of nfs-utils.
5  *
6  * nfs-utils is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * nfs-utils is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifndef NFS_UTILS_SOCKADDR_H
21 #define NFS_UTILS_SOCKADDR_H
22
23 #include <libio.h>
24 #include <stdbool.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27
28 /*
29  * This type is for defining buffers that contain network socket
30  * addresses.
31  *
32  * Casting a "struct sockaddr *" to the address of a "struct
33  * sockaddr_storage" breaks C aliasing rules.  The "union
34  * nfs_sockaddr" type follows C aliasing rules yet specifically
35  * allows converting pointers to it between "struct sockaddr *"
36  * and a few other network sockaddr-related pointer types.
37  *
38  * Note that this union is much smaller than a sockaddr_storage.
39  * It should be used only for AF_INET or AF_INET6 socket addresses.
40  * An AF_LOCAL sockaddr_un, for example, will clearly not fit into
41  * a buffer of this type.
42  */
43 union nfs_sockaddr {
44         struct sockaddr         sa;
45         struct sockaddr_in      s4;
46         struct sockaddr_in6     s6;
47 };
48
49 #if SIZEOF_SOCKLEN_T - 0 == 0
50 #define socklen_t unsigned int
51 #endif
52
53 #define SIZEOF_SOCKADDR_UNKNOWN (socklen_t)0
54 #define SIZEOF_SOCKADDR_IN      (socklen_t)sizeof(struct sockaddr_in)
55
56 #ifdef IPV6_SUPPORTED
57 #define SIZEOF_SOCKADDR_IN6     (socklen_t)sizeof(struct sockaddr_in6)
58 #else   /* !IPV6_SUPPORTED */
59 #define SIZEOF_SOCKADDR_IN6     SIZEOF_SOCKADDR_UNKNOWN
60 #endif  /* !IPV6_SUPPORTED */
61
62 /**
63  * nfs_sockaddr_length - return the size in bytes of a socket address
64  * @sap: pointer to socket address
65  *
66  * Returns the size in bytes of @sap, or zero if the family is
67  * not recognized.
68  */
69 static inline socklen_t
70 nfs_sockaddr_length(const struct sockaddr *sap)
71 {
72         switch (sap->sa_family) {
73         case AF_INET:
74                 return SIZEOF_SOCKADDR_IN;
75         case AF_INET6:
76                 return SIZEOF_SOCKADDR_IN6;
77         }
78         return SIZEOF_SOCKADDR_UNKNOWN;
79 }
80
81 static inline uint16_t
82 get_port4(const struct sockaddr *sap)
83 {
84         const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
85         return ntohs(sin->sin_port);
86 }
87
88 #ifdef IPV6_SUPPORTED
89 static inline uint16_t
90 get_port6(const struct sockaddr *sap)
91 {
92         const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap;
93         return ntohs(sin6->sin6_port);
94 }
95 #else   /* !IPV6_SUPPORTED */
96 static inline uint16_t
97 get_port6(__attribute__ ((unused)) const struct sockaddr *sap)
98 {
99         return 0;
100 }
101 #endif  /* !IPV6_SUPPORTED */
102
103 /**
104  * nfs_get_port - extract port value from a socket address
105  * @sap: pointer to socket address
106  *
107  * Returns port value in host byte order, or zero if the
108  * socket address contains an unrecognized family.
109  */
110 static inline uint16_t
111 nfs_get_port(const struct sockaddr *sap)
112 {
113         switch (sap->sa_family) {
114         case AF_INET:
115                 return get_port4(sap);
116         case AF_INET6:
117                 return get_port6(sap);
118         }
119         return 0;
120 }
121
122 static inline void
123 set_port4(struct sockaddr *sap, const uint16_t port)
124 {
125         struct sockaddr_in *sin = (struct sockaddr_in *)sap;
126         sin->sin_port = htons(port);
127 }
128
129 #ifdef IPV6_SUPPORTED
130 static inline void
131 set_port6(struct sockaddr *sap, const uint16_t port)
132 {
133         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
134         sin6->sin6_port = htons(port);
135 }
136 #else   /* !IPV6_SUPPORTED */
137 static inline void
138 set_port6(__attribute__ ((unused)) struct sockaddr *sap,
139                 __attribute__ ((unused)) const uint16_t port)
140 {
141 }
142 #endif  /* !IPV6_SUPPORTED */
143
144 /**
145  * nfs_set_port - set port value in a socket address
146  * @sap: pointer to socket address
147  * @port: port value to set
148  *
149  */
150 static inline void
151 nfs_set_port(struct sockaddr *sap, const uint16_t port)
152 {
153         switch (sap->sa_family) {
154         case AF_INET:
155                 set_port4(sap, port);
156                 break;
157         case AF_INET6:
158                 set_port6(sap, port);
159                 break;
160         }
161 }
162
163 /**
164  * nfs_is_v4_loopback - test to see if socket address is AF_INET loopback
165  * @sap: pointer to socket address
166  *
167  * Returns true if the socket address is the standard IPv4 loopback
168  * address; otherwise false is returned.
169  */
170 static inline _Bool
171 nfs_is_v4_loopback(const struct sockaddr *sap)
172 {
173         const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
174
175         if (sin->sin_family != AF_INET)
176                 return false;
177         if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
178                 return false;
179         return true;
180 }
181
182 static inline _Bool
183 compare_sockaddr4(const struct sockaddr *sa1, const struct sockaddr *sa2)
184 {
185         const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1;
186         const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2;
187         return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr;
188 }
189
190 #ifdef IPV6_SUPPORTED
191 static inline _Bool
192 compare_sockaddr6(const struct sockaddr *sa1, const struct sockaddr *sa2)
193 {
194         const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1;
195         const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2;
196
197         if ((IN6_IS_ADDR_LINKLOCAL((char *)&sin1->sin6_addr) &&
198              IN6_IS_ADDR_LINKLOCAL((char *)&sin2->sin6_addr)) ||
199             (IN6_IS_ADDR_SITELOCAL((char *)&sin1->sin6_addr) &&
200              IN6_IS_ADDR_SITELOCAL((char *)&sin2->sin6_addr)))
201                 if (sin1->sin6_scope_id != sin2->sin6_scope_id)
202                         return false;
203
204         return IN6_ARE_ADDR_EQUAL((char *)&sin1->sin6_addr,
205                                         (char *)&sin2->sin6_addr);
206 }
207 #else   /* !IPV6_SUPPORTED */
208 static inline _Bool
209 compare_sockaddr6(__attribute__ ((unused)) const struct sockaddr *sa1,
210                 __attribute__ ((unused)) const struct sockaddr *sa2)
211 {
212         return false;
213 }
214 #endif  /* !IPV6_SUPPORTED */
215
216 /**
217  * nfs_compare_sockaddr - compare two socket addresses for equality
218  * @sa1: pointer to a socket address
219  * @sa2: pointer to a socket address
220  *
221  * Returns true if the two socket addresses contain equivalent
222  * network addresses; otherwise false is returned.
223  */
224 static inline _Bool
225 nfs_compare_sockaddr(const struct sockaddr *sa1, const struct sockaddr *sa2)
226 {
227         if (sa1 == NULL || sa2 == NULL)
228                 return false;
229
230         if (sa1->sa_family == sa2->sa_family)
231                 switch (sa1->sa_family) {
232                 case AF_INET:
233                         return compare_sockaddr4(sa1, sa2);
234                 case AF_INET6:
235                         return compare_sockaddr6(sa1, sa2);
236                 }
237
238         return false;
239 }
240
241 #endif  /* !NFS_UTILS_SOCKADDR_H */