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