]> arthur.barton.de Git - ngircd-alex.git/blob - src/ipaddr/ng_ipaddr.c
IPv6 support.
[ngircd-alex.git] / src / ipaddr / ng_ipaddr.c
1 /*
2  * Functions for AF_ agnostic ipv4/ipv6 handling.
3  *
4  * (c) 2008 Florian Westphal <fw@strlen.de>, public domain.
5  */
6
7 #include "portab.h"
8
9 #include <assert.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #ifdef HAVE_GETADDRINFO
14 #include <netdb.h>
15 #include <sys/types.h>
16 #endif
17 #include <sys/socket.h>
18
19 #include <netinet/in.h>
20
21 #include "ng_ipaddr.h"
22
23 GLOBAL bool
24 ng_ipaddr_init(ng_ipaddr_t *addr, const char *ip_str, UINT16 port)
25 {
26 #ifdef HAVE_GETADDRINFO
27         int ret;
28         char portstr[64];
29         struct addrinfo *res0;
30         struct addrinfo hints = {
31 #ifndef WANT_IPV6       /* only accept v4 addresses */
32                 .ai_family = AF_INET,
33 #endif
34                 .ai_flags = AI_NUMERICHOST
35         };
36
37         if (ip_str == NULL)
38                 hints.ai_flags |= AI_PASSIVE;
39
40         /* silly, but ngircd stores UINT16 in server config, not string */
41         snprintf(portstr, sizeof(portstr), "%u", (unsigned int) port);
42         ret = getaddrinfo(ip_str, portstr, &hints, &res0);
43         assert(ret == 0);
44         if (ret != 0)
45                 return false;
46
47         assert(sizeof(*addr) >= res0->ai_addrlen);
48         if (sizeof(*addr) >= res0->ai_addrlen)
49                 memcpy(addr, res0->ai_addr, res0->ai_addrlen);
50         else
51                 ret = -1;
52         freeaddrinfo(res0);
53         return ret == 0;
54 #else /* HAVE_GETADDRINFO */
55         if (ip_str == NULL)
56                 ip_str = "0.0.0.0";
57         addr->sin4.sin_family = AF_INET;
58 # ifdef HAVE_INET_ATON
59         if (inet_aton(ip_str, &addr->sin4.sin_addr) == 0)
60                 return false;
61 # else
62         addr->sin4.sin_addr.s_addr = inet_addr(ip_str);
63         if (addr->sin4.sin_addr.s_addr == (unsigned) -1)
64                 return false;
65 # endif
66         ng_ipaddr_setport(addr, port);
67         return true;
68 #endif /* HAVE_GETADDRINFO */
69 }
70
71
72 GLOBAL void
73 ng_ipaddr_setport(ng_ipaddr_t *a, UINT16 port)
74 {
75 #ifdef WANT_IPV6
76         int af;
77
78         assert(a != NULL);
79
80         af = a->sa.sa_family;
81
82         assert(af == AF_INET || af == AF_INET6);
83
84         switch (af) {
85         case AF_INET:
86                 a->sin4.sin_port = htons(port);
87                 break;
88         case AF_INET6:
89                 a->sin6.sin6_port = htons(port);
90                 break;
91         }
92 #else /* WANT_IPV6 */
93         assert(a != NULL);
94         assert(a->sin4.sin_family == AF_INET);
95         a->sin4.sin_port = htons(port);
96 #endif /* WANT_IPV6 */
97 }
98
99
100
101 GLOBAL bool
102 ng_ipaddr_ipequal(const ng_ipaddr_t *a, const ng_ipaddr_t *b)
103 {
104         assert(a != NULL);
105         assert(b != NULL);
106 #ifdef WANT_IPV6
107         if (a->sa.sa_family != b->sa.sa_family)
108                 return false;
109         assert(ng_ipaddr_salen(a) == ng_ipaddr_salen(b));
110         switch (a->sa.sa_family) {
111         case AF_INET6:
112                 return IN6_ARE_ADDR_EQUAL(&a->sin6.sin6_addr, &b->sin6.sin6_addr);
113         case AF_INET:
114                 return memcmp(&a->sin4.sin_addr, &b->sin4.sin_addr, sizeof(a->sin4.sin_addr)) == 0;
115         }
116         return false;
117 #else
118         assert(a->sin4.sin_family == AF_INET);
119         assert(b->sin4.sin_family == AF_INET);
120         return memcmp(&a->sin4.sin_addr, &b->sin4.sin_addr, sizeof(a->sin4.sin_addr)) == 0;
121 #endif
122 }
123
124
125 #ifdef WANT_IPV6
126 GLOBAL const char *
127 ng_ipaddr_tostr(const ng_ipaddr_t *addr)
128 {
129         static char strbuf[NG_INET_ADDRSTRLEN];
130
131         strbuf[0] = 0;
132
133         ng_ipaddr_tostr_r(addr, strbuf);
134         return strbuf;
135 }
136
137
138 /* str must be at least NG_INET_ADDRSTRLEN bytes long */
139 GLOBAL bool
140 ng_ipaddr_tostr_r(const ng_ipaddr_t *addr, char *str)
141 {
142 #ifdef HAVE_GETNAMEINFO
143         const struct sockaddr *sa = (const struct sockaddr *) addr;
144         int ret;
145
146         *str = 0;
147
148         ret = getnameinfo(sa, ng_ipaddr_salen(addr),
149                         str, NG_INET_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
150         /*
151          * avoid leading ':'.
152          * causes mis-interpretation of client host in e.g. /WHOIS
153          */
154         if (*str == ':') {
155                 char tmp[NG_INET_ADDRSTRLEN] = "0";
156                 ret = getnameinfo(sa, ng_ipaddr_salen(addr),
157                                 tmp+1, sizeof(tmp) -1, NULL, 0, NI_NUMERICHOST);
158                 if (ret == 0)
159                         strlcpy(str, tmp, NG_INET_ADDRSTRLEN);
160         }
161         assert (ret == 0);
162         return ret == 0;
163 #else
164         abort(); /* WANT_IPV6 depends on HAVE_GETNAMEINFO */
165 #endif
166 }
167
168 #endif /* WANT_IPV6 */
169
170 /* -eof- */