1 /* Copyright (c) 2007-2009, UNINETT AS
2 * Copyright (c) 2016, NORDUnet A/S */
3 /* See LICENSE for licensing information. */
4
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <netinet/tcp.h>
8 #include <netdb.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <errno.h>
15 #include <poll.h>
16 #include <stdarg.h>
17 #include <assert.h>
18 #include "debug.h"
19 #include "util.h"
20
stringcopy(const char * s,int len)21 char *stringcopy(const char *s, int len) {
22 char *r;
23 if (!s)
24 return NULL;
25 if (!len)
26 len = strlen(s);
27 r = malloc(len + 1);
28 if (!r)
29 debugx(1, DBG_ERR, "stringcopy: malloc failed");
30 memcpy(r, s, len);
31 r[len] = '\0';
32 return r;
33 }
34
printfchars(char * prefixfmt,char * prefix,char * charfmt,uint8_t * chars,int len)35 void printfchars(char *prefixfmt, char *prefix, char *charfmt, uint8_t *chars, int len) {
36 int i;
37 unsigned char *s = (unsigned char *)chars;
38 if (prefix)
39 printf(prefixfmt ? prefixfmt : "%s: ", prefix);
40 for (i = 0; i < len; i++)
41 printf(charfmt ? charfmt : "%c", s[i]);
42 printf("\n");
43 }
44
port_set(struct sockaddr * sa,uint16_t port)45 void port_set(struct sockaddr *sa, uint16_t port) {
46 switch (sa->sa_family) {
47 case AF_INET:
48 ((struct sockaddr_in *)sa)->sin_port = htons(port);
49 break;
50 case AF_INET6:
51 ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
52 break;
53 }
54 }
55
addr_copy(struct sockaddr * in)56 struct sockaddr *addr_copy(struct sockaddr *in) {
57 struct sockaddr *out = NULL;
58
59 switch (in->sa_family) {
60 case AF_INET:
61 out = malloc(sizeof(struct sockaddr_in));
62 if (out == NULL)
63 return NULL;
64 *(struct sockaddr_in *)out = *(struct sockaddr_in *)in;
65 break;
66 case AF_INET6:
67 out = malloc(sizeof(struct sockaddr_in6));
68 if (out == NULL)
69 return NULL;
70 *(struct sockaddr_in6 *)out = *(struct sockaddr_in6 *)in;
71 break;
72 }
73 assert(out);
74 #ifdef SIN6_LEN
75 out->sa_len = in->sa_len;
76 #endif
77 return out;
78 }
79
addr2string(struct sockaddr * addr,char * buf,size_t len)80 const char *addr2string(struct sockaddr *addr, char *buf, size_t len) {
81 struct sockaddr_in6 *sa6;
82 struct sockaddr_in sa4;
83
84 if (addr->sa_family == AF_INET6) {
85 sa6 = (struct sockaddr_in6 *)addr;
86 if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
87 memset(&sa4, 0, sizeof(sa4));
88 sa4.sin_family = AF_INET;
89 sa4.sin_port = sa6->sin6_port;
90 memcpy(&sa4.sin_addr, &sa6->sin6_addr.s6_addr[12], 4);
91 addr = (struct sockaddr *)&sa4;
92 }
93 }
94 if (getnameinfo(addr, SOCKADDRP_SIZE(addr), buf, len,
95 NULL, 0, NI_NUMERICHOST)) {
96 debug(DBG_WARN, "getnameinfo failed");
97 return "getnameinfo_failed";
98 }
99 return buf;
100 }
101
102 /* Disable the "Don't Fragment" bit for UDP sockets. It is set by default, which may cause an "oversized"
103 RADIUS packet to be discarded on first attempt (due to Path MTU discovery).
104 */
105
disable_DF_bit(int socket,struct addrinfo * res)106 void disable_DF_bit(int socket, struct addrinfo *res) {
107 if ((res->ai_family == AF_INET) && (res->ai_socktype == SOCK_DGRAM)) {
108 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
109 /*
110 * Turn off Path MTU discovery on IPv4/UDP sockets, Linux variant.
111 */
112 int r, action;
113 debug(DBG_INFO, "disable_DF_bit: disabling DF bit (Linux variant)");
114 action = IP_PMTUDISC_DONT;
115 r = setsockopt(socket, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action));
116 if (r == -1)
117 debug(DBG_WARN, "Failed to set IP_MTU_DISCOVER");
118 #else
119 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!");
120 #endif
121 }
122 }
123
enable_keepalive(int socket)124 void enable_keepalive(int socket) {
125 int optval;
126 socklen_t optlen = sizeof(optval);
127
128 #if !defined(TCP_KEEPCNT) || !defined(TCP_KEEPIDLE) || !defined(TCP_KEEPINTVL)
129 debug(DBG_NOTICE, "TCP Keepalive feature might be limited on this platform");
130 #else
131 optval = 3;
132 if(setsockopt(socket, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen) < 0) {
133 debug(DBG_ERR, "enable_keepalive: setsockopt TCP_KEEPCNT failed");
134 }
135 optval = 10;
136 if(setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen) < 0) {
137 debug(DBG_ERR, "enable_keepalive: setsockopt TCP_KEEPIDLE %d failed", optval);
138 }
139 optval = 10;
140 if(setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen) < 0) {
141 debug(DBG_ERR, "enable_keepalive: setsockopt TCP_KEEPINTVL failed");
142 }
143 #endif
144 optval = 1;
145 if(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
146 debug(DBG_ERR, "enable_keepalive: setsockopt SO_KEEPALIVE failed");
147 }
148 }
149
bindtoaddr(struct addrinfo * addrinfo,int family,int reuse)150 int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse) {
151 int s, on = 1;
152 struct addrinfo *res;
153
154 for (res = addrinfo; res; res = res->ai_next) {
155 if (family != AF_UNSPEC && family != res->ai_family)
156 continue;
157 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
158 if (s < 0) {
159 debug(DBG_WARN, "bindtoaddr: socket failed");
160 continue;
161 }
162
163 disable_DF_bit(s,res);
164
165 if (reuse)
166 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
167 debugerrno(errno, DBG_WARN, "Failed to set SO_REUSEADDR");
168 #ifdef IPV6_V6ONLY
169 if (family == AF_INET6)
170 if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1)
171 debugerrno(errno, DBG_WARN, "Failed to set IPV6_V6ONLY");
172 #endif
173 if (!bind(s, res->ai_addr, res->ai_addrlen))
174 return s;
175 debug(DBG_WARN, "bindtoaddr: bind failed");
176 close(s);
177 }
178 return -1;
179 }
180
connectnonblocking(int s,const struct sockaddr * addr,socklen_t addrlen,int timeout)181 int connectnonblocking(int s, const struct sockaddr *addr, socklen_t addrlen, int timeout) {
182 int origflags, r = -1, sockerr = 0;
183 socklen_t errlen = sizeof(sockerr);
184 struct pollfd fds[1];
185
186 origflags = fcntl(s, F_GETFL, 0);
187 if (origflags == -1) {
188 debugerrno(errno, DBG_WARN, "Failed to get flags");
189 return -1;
190 }
191 if (fcntl(s, F_SETFL, origflags | O_NONBLOCK) == -1) {
192 debugerrno(errno, DBG_WARN, "Failed to set O_NONBLOCK");
193 return -1;
194 }
195 if (!connect(s, addr, addrlen)) {
196 r = 0;
197 goto exit;
198 }
199 if (errno != EINPROGRESS)
200 goto exit;
201
202 fds[0].fd = s;
203 fds[0].events = POLLOUT;
204 if (poll(fds, 1, timeout * 1000) < 1)
205 goto exit;
206
207 if (fds[0].revents & POLLERR) {
208 if(!getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &errlen))
209 debug(DBG_WARN, "Connection failed: %s", strerror(sockerr));
210 else
211 debug(DBG_WARN, "Connection failed: unknown error");
212 } else if (fds[0].revents & POLLHUP) {
213 debug(DBG_WARN, "Connect error: hang up");
214 } else if (fds[0].revents & POLLNVAL) {
215 debug(DBG_WARN, "Connect error: fd not open");
216 } else if(fds[0].revents & POLLOUT) {
217 debug(DBG_DBG, "Connection up");
218 r = 0;
219 }
220
221 exit:
222 if (fcntl(s, F_SETFL, origflags) == -1)
223 debugerrno(errno, DBG_WARN, "Failed to set original flags back");
224 return r;
225 }
226
connecttcp(struct addrinfo * addrinfo,struct addrinfo * src,uint16_t timeout)227 int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout) {
228 int s;
229 struct addrinfo *res;
230
231 s = -1;
232 if (timeout) {
233 if (addrinfo && addrinfo->ai_next && timeout > 5)
234 timeout = 5;
235 }
236
237 for (res = addrinfo; res; res = res->ai_next) {
238 s = bindtoaddr(src, res->ai_family, 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, timeout)
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 }
253
connect_wait(struct timeval attempt_start,struct timeval last_success,int firsttry)254 time_t connect_wait(struct timeval attempt_start, struct timeval last_success, int firsttry) {
255 struct timeval now;
256
257 gettimeofday(&now, NULL);
258
259 if (attempt_start.tv_sec < last_success.tv_sec ||
260 attempt_start.tv_sec > now.tv_sec) {
261 debug(DBG_WARN, "connect_wait: invalid timers detected!");
262 return 60;
263 }
264
265 if (now.tv_sec - last_success.tv_sec < 30)
266 return 30 - (attempt_start.tv_sec - last_success.tv_sec);
267
268 if (firsttry)
269 return 0;
270
271 if (now.tv_sec - attempt_start.tv_sec < 2)
272 return 2;
273
274 if (now.tv_sec - attempt_start.tv_sec > 60)
275 return 60;
276
277 return now.tv_sec - attempt_start.tv_sec;
278 }
279
280 /* Local Variables: */
281 /* c-file-style: "stroustrup" */
282 /* End: */
283