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