radsecproxy-1.6.5.
[libradsec.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 /* Disable the "Don't Fragment" bit for UDP sockets. It is set by default, which may cause an "oversized"
114    RADIUS packet to be discarded on first attempt (due to Path MTU discovery).
115 */
116
117 void disable_DF_bit(int socket, struct addrinfo *res) {
118     if ((res->ai_family == AF_INET) && (res->ai_socktype == SOCK_DGRAM)) {
119 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
120         /*
121          * Turn off Path MTU discovery on IPv4/UDP sockets, Linux variant.
122          */
123         int r, action;
124         debug(DBG_INFO, "disable_DF_bit: disabling DF bit (Linux variant)");
125         action = IP_PMTUDISC_DONT;
126         r = setsockopt(socket, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action));
127         if (r == -1)
128             debug(DBG_WARN, "Failed to set IP_MTU_DISCOVER");
129 #else
130         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!");
131 #endif
132     }
133 }
134
135 int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) {
136     int s, on = 1;
137     struct addrinfo *res;
138
139     for (res = addrinfo; res; res = res->ai_next) {
140         if (family != AF_UNSPEC && family != res->ai_family)
141             continue;
142         s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
143         if (s < 0) {
144             debug(DBG_WARN, "bindtoaddr: socket failed");
145             continue;
146         }
147
148         disable_DF_bit(s,res);
149
150         if (reuse)
151             setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
152 #ifdef IPV6_V6ONLY
153         if (v6only)
154             setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
155 #endif
156         if (!bind(s, res->ai_addr, res->ai_addrlen))
157             return s;
158         debug(DBG_WARN, "bindtoaddr: bind failed");
159         close(s);
160     }
161     return -1;
162 }
163
164 int connectnonblocking(int s, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout) {
165     int origflags, error = 0, r = -1;
166     fd_set writefds;
167     socklen_t len;
168
169     origflags = fcntl(s, F_GETFL, 0);
170     fcntl(s, F_SETFL, origflags | O_NONBLOCK);
171     if (!connect(s, addr, addrlen)) {
172         r = 0;
173         goto exit;
174     }
175     if (errno != EINPROGRESS)
176         goto exit;
177
178     FD_ZERO(&writefds);
179     FD_SET(s, &writefds);
180     if (select(s + 1, NULL, &writefds, NULL, timeout) < 1)
181         goto exit;
182
183     len = sizeof(error);
184     if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&error, &len) && !error)
185         r = 0;
186
187 exit:
188     fcntl(s, F_SETFL, origflags);
189     return r;
190 }
191
192 int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout) {
193     int s;
194     struct addrinfo *res;
195     struct timeval to;
196
197     s = -1;
198     if (timeout) {
199         if (addrinfo && addrinfo->ai_next && timeout > 5)
200             timeout = 5;
201         to.tv_sec = timeout;
202         to.tv_usec = 0;
203     }
204
205     for (res = addrinfo; res; res = res->ai_next) {
206         s = bindtoaddr(src, res->ai_family, 1, 1);
207         if (s < 0) {
208             debug(DBG_WARN, "connecttoserver: socket failed");
209             continue;
210         }
211         if ((timeout
212              ? connectnonblocking(s, res->ai_addr, res->ai_addrlen, &to)
213              : connect(s, res->ai_addr, res->ai_addrlen)) == 0)
214             break;
215         debug(DBG_WARN, "connecttoserver: connect failed");
216         close(s);
217         s = -1;
218     }
219     return s;
220 }
221
222 /* Local Variables: */
223 /* c-file-style: "stroustrup" */
224 /* End: */