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