util.c 5.87 KB
Newer Older
1
/*
venaas's avatar
venaas committed
2
 * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
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
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>
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>
24
#include <stdarg.h>
venaas's avatar
venaas committed
25
#include "debug.h"
26
#include "util.h"
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;
83 84 85
#ifdef SIN6_LEN
    out->sa_len = in->sa_len;
#endif
86 87 88
    return out;
}

89
char *addr2string(struct sockaddr *addr) {
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]),
106
                    NULL, 0, NI_NUMERICHOST)) {
107
        debug(DBG_WARN, "getnameinfo failed");
venaas's avatar
venaas committed
108
        return "getnameinfo_failed";
109 110 111 112
    }
    return addr_buf[i];
}

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)) {
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");
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
    }
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;
	}
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: */