util.c 5.87 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");
37 38 39 40
    memcpy(r, s, len);
    r[len] = '\0';
    return r;
}
41 42 43 44 45 46 47 48 49 50 51

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
52 53 54 55 56 57 58 59 60 61 62
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;
    }
}

63 64
struct sockaddr *addr_copy(struct sockaddr *in) {
    struct sockaddr *out = NULL;
65

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

89
char *addr2string(struct sockaddr *addr) {
venaas's avatar
 
venaas committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    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;
	}
    }
105
    if (getnameinfo(addr, SOCKADDRP_SIZE(addr), addr_buf[i], sizeof(addr_buf[i]),
venaas's avatar
 
venaas committed
106
                    NULL, 0, NI_NUMERICHOST)) {
107
        debug(DBG_WARN, "getnameinfo failed");
venaas's avatar
venaas committed
108
        return "getnameinfo_failed";
venaas's avatar
 
venaas committed
109 110 111 112
    }
    return addr_buf[i];
}

kolla's avatar
kolla committed
113 114 115 116 117
/* 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) {
118
    if ((res->ai_family == AF_INET) && (res->ai_socktype == SOCK_DGRAM)) {
kolla's avatar
kolla committed
119 120 121 122 123 124 125 126
#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));
127 128
        if (r == -1)
	    debug(DBG_WARN, "Failed to set IP_MTU_DISCOVER");
kolla's avatar
kolla committed
129 130 131
#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
132
    }
kolla's avatar
kolla committed
133 134
}

135 136 137 138 139 140 141 142 143 144 145 146
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
147 148 149

	disable_DF_bit(s,res);

150 151
	if (reuse)
	    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
152
#ifdef IPV6_V6ONLY
153 154
	if (v6only)
	    setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
155
#endif
156 157 158 159 160 161 162 163
	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
164 165 166 167
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;
168

venaas's avatar
venaas committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    origflags = fcntl(s, F_GETFL, 0);
    fcntl(s, F_SETFL, origflags | O_NONBLOCK);
    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;

187
exit:
venaas's avatar
venaas committed
188 189 190 191 192
    fcntl(s, F_SETFL, origflags);
    return r;
}

int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout) {
193 194
    int s;
    struct addrinfo *res;
venaas's avatar
venaas committed
195
    struct timeval to;
196 197

    s = -1;
venaas's avatar
venaas committed
198
    if (timeout) {
199
	if (addrinfo && addrinfo->ai_next && timeout > 5)
venaas's avatar
venaas committed
200 201 202 203
	    timeout = 5;
	to.tv_sec = timeout;
	to.tv_usec = 0;
    }
204

205 206 207 208 209 210
    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
211 212 213
	if ((timeout
	     ? connectnonblocking(s, res->ai_addr, res->ai_addrlen, &to)
	     : connect(s, res->ai_addr, res->ai_addrlen)) == 0)
214 215 216 217 218 219 220
	    break;
	debug(DBG_WARN, "connecttoserver: connect failed");
	close(s);
	s = -1;
    }
    return s;
}
221 222 223 224

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