util.c 6.43 KB
Newer Older
venaas's avatar
 
venaas committed
1
/*
venaas's avatar
venaas committed
2
 * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
venaas's avatar
 
venaas committed
3
4
5
6
7
8
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */

kolla's avatar
Credits    
kolla committed
9
10
11
12
13
/* Code contributions from:
 *
 * Stefan Winter <stefan.winter@restena.lu>
 */

venaas's avatar
venaas committed
14
15
#include <sys/socket.h>
#include <netinet/in.h>
venaas's avatar
 
venaas committed
16
17
18
19
20
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
venaas's avatar
venaas committed
21
22
23
#include <fcntl.h>
#include <errno.h>
#include <sys/select.h>
venaas's avatar
 
venaas committed
24
#include <stdarg.h>
venaas's avatar
venaas committed
25
#include "debug.h"
26
#include "util.h"
venaas's avatar
 
venaas committed
27

28
char *stringcopy(const char *s, int len) {
29
    char *r;
30
31
    if (!s)
	return NULL;
32
33
34
35
    if (!len)
	len = strlen(s);
    r = malloc(len + 1);
    if (!r)
venaas's avatar
venaas committed
36
	debug(DBG_ERR, "stringcopy: malloc failed");
Linus Nordberg's avatar
Linus Nordberg committed
37
38
39
40
    else {
        memcpy(r, s, len);
        r[len] = '\0';
    }
41
42
    return r;
}
43
44
45
46
47
48
49
50
51
52
53

void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len) {
    int i;
    unsigned char *s = (unsigned char *)chars;
    if (prefix)
	printf(prefixfmt ? prefixfmt : "%s: ", prefix);
    for (i = 0; i < len; i++)
	printf(charfmt ? charfmt : "%c", s[i]);
    printf("\n");
}

venaas's avatar
venaas committed
54
55
56
57
58
59
60
61
62
63
64
void port_set(struct sockaddr *sa, uint16_t port) {
    switch (sa->sa_family) {
    case AF_INET:
	((struct sockaddr_in *)sa)->sin_port = htons(port);
	break;
    case AF_INET6:
	((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
	break;
    }
}

65
66
struct sockaddr *addr_copy(struct sockaddr *in) {
    struct sockaddr *out = NULL;
67

68
69
70
    switch (in->sa_family) {
    case AF_INET:
	out = malloc(sizeof(struct sockaddr_in));
71
72
73
74
        if (out == NULL)
            return NULL;
        memset(out, 0, sizeof(struct sockaddr_in));
        ((struct sockaddr_in *)out)->sin_addr = ((struct sockaddr_in *)in)->sin_addr;
75
76
77
	break;
    case AF_INET6:
	out = malloc(sizeof(struct sockaddr_in6));
78
79
80
81
        if (out == NULL)
            return NULL;
        memset(out, 0, sizeof(struct sockaddr_in6));
        ((struct sockaddr_in6 *)out)->sin6_addr = ((struct sockaddr_in6 *)in)->sin6_addr;
82
83
	break;
    }
venaas's avatar
venaas committed
84
    out->sa_family = in->sa_family;
venaas's avatar
venaas committed
85
86
87
#ifdef SIN6_LEN
    out->sa_len = in->sa_len;
#endif
88
89
90
    return out;
}

91
char *addr2string(struct sockaddr *addr) {
venaas's avatar
 
venaas committed
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    struct sockaddr_in6 *sa6;
    struct sockaddr_in sa4;
    static char addr_buf[2][INET6_ADDRSTRLEN];
    static int i = 0;
    i = !i;
    if (addr->sa_family == AF_INET6) {
	sa6 = (struct sockaddr_in6 *)addr;
	if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
	    memset(&sa4, 0, sizeof(sa4));
	    sa4.sin_family = AF_INET;
	    sa4.sin_port = sa6->sin6_port;
	    memcpy(&sa4.sin_addr, &sa6->sin6_addr.s6_addr[12], 4);
	    addr = (struct sockaddr *)&sa4;
	}
    }
107
    if (getnameinfo(addr, SOCKADDRP_SIZE(addr), addr_buf[i], sizeof(addr_buf[i]),
venaas's avatar
 
venaas committed
108
                    NULL, 0, NI_NUMERICHOST)) {
109
        debug(DBG_WARN, "getnameinfo failed");
venaas's avatar
venaas committed
110
        return "getnameinfo_failed";
venaas's avatar
 
venaas committed
111
112
113
114
    }
    return addr_buf[i];
}

kolla's avatar
kolla committed
115
116
117
118
119
/* Disable the "Don't Fragment" bit for UDP sockets. It is set by default, which may cause an "oversized"
   RADIUS packet to be discarded on first attempt (due to Path MTU discovery).
*/

void disable_DF_bit(int socket, struct addrinfo *res) {
120
    if ((res->ai_family == AF_INET) && (res->ai_socktype == SOCK_DGRAM)) {
kolla's avatar
kolla committed
121
122
123
124
125
126
127
128
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
        /*
         * Turn off Path MTU discovery on IPv4/UDP sockets, Linux variant.
         */
	int r, action;
        debug(DBG_INFO, "disable_DF_bit: disabling DF bit (Linux variant)");
        action = IP_PMTUDISC_DONT;
        r = setsockopt(socket, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action));
129
130
        if (r == -1)
	    debug(DBG_WARN, "Failed to set IP_MTU_DISCOVER");
kolla's avatar
kolla committed
131
132
133
#else
	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!");
#endif
134
    }
kolla's avatar
kolla committed
135
136
}

137
138
139
140
141
142
143
144
145
146
147
148
int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) {
    int s, on = 1;
    struct addrinfo *res;

    for (res = addrinfo; res; res = res->ai_next) {
	if (family != AF_UNSPEC && family != res->ai_family)
	    continue;
	s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if (s < 0) {
	    debug(DBG_WARN, "bindtoaddr: socket failed");
	    continue;
	}
kolla's avatar
kolla committed
149
150
151

	disable_DF_bit(s,res);

152
	if (reuse)
153
154
	    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
                debugerrno(errno, DBG_WARN, "Failed to set SO_REUSEADDR");
155
#ifdef IPV6_V6ONLY
156
	if (v6only)
157
158
	    if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1)
                debugerrno(errno, DBG_WARN, "Failed to set IPV6_V6ONLY");
159
#endif
160
161
162
163
164
165
166
167
	if (!bind(s, res->ai_addr, res->ai_addrlen))
	    return s;
	debug(DBG_WARN, "bindtoaddr: bind failed");
	close(s);
    }
    return -1;
}

venaas's avatar
venaas committed
168
169
170
171
int connectnonblocking(int s, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout) {
    int origflags, error = 0, r = -1;
    fd_set writefds;
    socklen_t len;
172

venaas's avatar
venaas committed
173
    origflags = fcntl(s, F_GETFL, 0);
174
175
176
177
178
179
180
181
    if (origflags == -1) {
        debugerrno(errno, DBG_WARN, "Failed to get flags");
        return -1;
    }
    if (fcntl(s, F_SETFL, origflags | O_NONBLOCK) == -1) {
        debugerrno(errno, DBG_WARN, "Failed to set O_NONBLOCK");
        return -1;
    }
venaas's avatar
venaas committed
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
    if (!connect(s, addr, addrlen)) {
	r = 0;
	goto exit;
    }
    if (errno != EINPROGRESS)
	goto exit;

    FD_ZERO(&writefds);
    FD_SET(s, &writefds);
    if (select(s + 1, NULL, &writefds, NULL, timeout) < 1)
	goto exit;

    len = sizeof(error);
    if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&error, &len) && !error)
	r = 0;

198
exit:
199
200
    if (fcntl(s, F_SETFL, origflags) == -1)
        debugerrno(errno, DBG_WARN, "Failed to set original flags back");
venaas's avatar
venaas committed
201
202
203
204
    return r;
}

int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout) {
205
206
    int s;
    struct addrinfo *res;
venaas's avatar
venaas committed
207
    struct timeval to;
208
209

    s = -1;
venaas's avatar
venaas committed
210
    if (timeout) {
211
	if (addrinfo && addrinfo->ai_next && timeout > 5)
venaas's avatar
venaas committed
212
213
214
215
	    timeout = 5;
	to.tv_sec = timeout;
	to.tv_usec = 0;
    }
216

217
218
219
220
221
222
    for (res = addrinfo; res; res = res->ai_next) {
	s = bindtoaddr(src, res->ai_family, 1, 1);
	if (s < 0) {
	    debug(DBG_WARN, "connecttoserver: socket failed");
	    continue;
	}
venaas's avatar
venaas committed
223
224
225
	if ((timeout
	     ? connectnonblocking(s, res->ai_addr, res->ai_addrlen, &to)
	     : connect(s, res->ai_addr, res->ai_addrlen)) == 0)
226
227
228
229
230
231
232
	    break;
	debug(DBG_WARN, "connecttoserver: connect failed");
	close(s);
	s = -1;
    }
    return s;
}
233
234
235
236

/* Local Variables: */
/* c-file-style: "stroustrup" */
/* End: */