Credits
[radsecproxy.git] / util.c
1 /*
2  * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  */
8
9 /* Code contributions from:
10  *
11  * Stefan Winter <stefan.winter@restena.lu>
12  */
13
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <netdb.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <sys/select.h>
24 #include <stdarg.h>
25 #include "debug.h"
26 #include "util.h"
27
28 char *stringcopy(const char *s, int len) {
29     char *r;
30     if (!s)
31         return NULL;
32     if (!len)
33         len = strlen(s);
34     r = malloc(len + 1);
35     if (!r)
36         debug(DBG_ERR, "stringcopy: malloc failed");
37     memcpy(r, s, len);
38     r[len] = '\0';
39     return r;
40 }
41
42 void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len) {
43     int i;
44     unsigned char *s = (unsigned char *)chars;
45     if (prefix)
46         printf(prefixfmt ? prefixfmt : "%s: ", prefix);
47     for (i = 0; i < len; i++)
48         printf(charfmt ? charfmt : "%c", s[i]);
49     printf("\n");
50 }
51
52 void port_set(struct sockaddr *sa, uint16_t port) {
53     switch (sa->sa_family) {
54     case AF_INET:
55         ((struct sockaddr_in *)sa)->sin_port = htons(port);
56         break;
57     case AF_INET6:
58         ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
59         break;
60     }
61 }
62
63 struct sockaddr *addr_copy(struct sockaddr *in) {
64     struct sockaddr *out = NULL;
65     
66     switch (in->sa_family) {
67     case AF_INET:
68         out = malloc(sizeof(struct sockaddr_in));
69         if (out) {
70             memset(out, 0, sizeof(struct sockaddr_in));
71             ((struct sockaddr_in *)out)->sin_addr = ((struct sockaddr_in *)in)->sin_addr;
72         }
73         break;
74     case AF_INET6:
75         out = malloc(sizeof(struct sockaddr_in6));
76         if (out) {
77             memset(out, 0, sizeof(struct sockaddr_in6));
78             ((struct sockaddr_in6 *)out)->sin6_addr = ((struct sockaddr_in6 *)in)->sin6_addr;
79         }
80         break;
81     }
82     out->sa_family = in->sa_family;
83 #ifdef SIN6_LEN
84     out->sa_len = in->sa_len;
85 #endif
86     return out;
87 }
88
89 char *addr2string(struct sockaddr *addr) {
90     struct sockaddr_in6 *sa6;
91     struct sockaddr_in sa4;
92     static char addr_buf[2][INET6_ADDRSTRLEN];
93     static int i = 0;
94     i = !i;
95     if (addr->sa_family == AF_INET6) {
96         sa6 = (struct sockaddr_in6 *)addr;
97         if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
98             memset(&sa4, 0, sizeof(sa4));
99             sa4.sin_family = AF_INET;
100             sa4.sin_port = sa6->sin6_port;
101             memcpy(&sa4.sin_addr, &sa6->sin6_addr.s6_addr[12], 4);
102             addr = (struct sockaddr *)&sa4;
103         }
104     }
105     if (getnameinfo(addr, SOCKADDRP_SIZE(addr), addr_buf[i], sizeof(addr_buf[i]),
106                     NULL, 0, NI_NUMERICHOST)) {
107         debug(DBG_WARN, "getnameinfo failed");
108         return "getnameinfo_failed";
109     }
110     return addr_buf[i];
111 }
112
113 #if 0
114 /* not in use */
115 int connectport(int type, char *host, char *port) {
116     struct addrinfo hints, *res0, *res;
117     int s = -1;
118     
119     memset(&hints, 0, sizeof(hints));
120     hints.ai_socktype = type;
121     hints.ai_family = AF_UNSPEC;
122
123     if (getaddrinfo(host, port, &hints, &res0) != 0) {
124         debug(DBG_ERR, "connectport: can't resolve host %s port %s", host, port);
125         return -1;
126     }
127
128     for (res = res0; res; res = res->ai_next) {
129         s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
130         if (s < 0) {
131             debug(DBG_WARN, "connectport: socket failed");
132             continue;
133         }
134         if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
135             break;
136         debug(DBG_WARN, "connectport: connect failed");
137         close(s);
138         s = -1;
139     }
140     freeaddrinfo(res0);
141     return s;
142 }
143 #endif
144
145 /* Disable the "Don't Fragment" bit for UDP sockets. It is set by default, which may cause an "oversized"
146    RADIUS packet to be discarded on first attempt (due to Path MTU discovery).
147 */
148
149 void disable_DF_bit(int socket, struct addrinfo *res) {
150         if ((res->ai_family == AF_INET) && (res->ai_socktype == SOCK_DGRAM)) {
151 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
152         /*
153          * Turn off Path MTU discovery on IPv4/UDP sockets, Linux variant.
154          */
155         int r, action;
156         debug(DBG_INFO, "disable_DF_bit: disabling DF bit (Linux variant)");
157         action = IP_PMTUDISC_DONT;
158         r = setsockopt(socket, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action));
159         if (r == -1) 
160                   debug(DBG_WARN, "Failed to set IP_MTU_DISCOVER");
161 #else
162         debug(DBG_INFO, "Non-Linux platform, unable to unset DF bit for UDP. You should check with tcpdump whether radsecproxy will send its UDP packets with DF bit set!");
163 #endif
164         }
165 }
166
167 int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) {
168     int s, on = 1;
169     struct addrinfo *res;
170
171     for (res = addrinfo; res; res = res->ai_next) {
172         if (family != AF_UNSPEC && family != res->ai_family)
173             continue;
174         s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
175         if (s < 0) {
176             debug(DBG_WARN, "bindtoaddr: socket failed");
177             continue;
178         }
179
180         disable_DF_bit(s,res);
181
182         if (reuse)
183             setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
184         #ifdef IPV6_V6ONLY
185         if (v6only)
186             setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
187         #endif
188         if (!bind(s, res->ai_addr, res->ai_addrlen))
189             return s;
190         debug(DBG_WARN, "bindtoaddr: bind failed");
191         close(s);
192     }
193     return -1;
194 }
195
196 int connectnonblocking(int s, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout) {
197     int origflags, error = 0, r = -1;
198     fd_set writefds;
199     socklen_t len;
200     
201     origflags = fcntl(s, F_GETFL, 0);
202     fcntl(s, F_SETFL, origflags | O_NONBLOCK);
203     if (!connect(s, addr, addrlen)) {
204         r = 0;
205         goto exit;
206     }
207     if (errno != EINPROGRESS)
208         goto exit;
209
210     FD_ZERO(&writefds);
211     FD_SET(s, &writefds);
212     if (select(s + 1, NULL, &writefds, NULL, timeout) < 1)
213         goto exit;
214
215     len = sizeof(error);
216     if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&error, &len) && !error)
217         r = 0;
218
219  exit:    
220     fcntl(s, F_SETFL, origflags);
221     return r;
222 }
223
224 int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout) {
225     int s;
226     struct addrinfo *res;
227     struct timeval to;
228
229     s = -1;
230     if (timeout) {
231         if (addrinfo && addrinfo->ai_next && timeout > 5)
232             timeout = 5;
233         to.tv_sec = timeout;
234         to.tv_usec = 0;
235     }
236     
237     for (res = addrinfo; res; res = res->ai_next) {
238         s = bindtoaddr(src, res->ai_family, 1, 1);
239         if (s < 0) {
240             debug(DBG_WARN, "connecttoserver: socket failed");
241             continue;
242         }
243         if ((timeout
244              ? connectnonblocking(s, res->ai_addr, res->ai_addrlen, &to)
245              : connect(s, res->ai_addr, res->ai_addrlen)) == 0)
246             break;
247         debug(DBG_WARN, "connecttoserver: connect failed");
248         close(s);
249         s = -1;
250     }
251     return s;
252 }