fixed addr_copy function for bsd
[radsecproxy.git] / 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 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <netdb.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <sys/select.h>
19 #include <stdarg.h>
20 #include "debug.h"
21 #include "util.h"
22
23 char *stringcopy(const char *s, int len) {
24     char *r;
25     if (!s)
26         return NULL;
27     if (!len)
28         len = strlen(s);
29     r = malloc(len + 1);
30     if (!r)
31         debug(DBG_ERR, "stringcopy: malloc failed");
32     memcpy(r, s, len);
33     r[len] = '\0';
34     return r;
35 }
36
37 void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len) {
38     int i;
39     unsigned char *s = (unsigned char *)chars;
40     if (prefix)
41         printf(prefixfmt ? prefixfmt : "%s: ", prefix);
42     for (i = 0; i < len; i++)
43         printf(charfmt ? charfmt : "%c", s[i]);
44     printf("\n");
45 }
46
47 void port_set(struct sockaddr *sa, uint16_t port) {
48     switch (sa->sa_family) {
49     case AF_INET:
50         ((struct sockaddr_in *)sa)->sin_port = htons(port);
51         break;
52     case AF_INET6:
53         ((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
54         break;
55     }
56 }
57
58 struct sockaddr *addr_copy(struct sockaddr *in) {
59     struct sockaddr *out = NULL;
60     
61     switch (in->sa_family) {
62     case AF_INET:
63         out = malloc(sizeof(struct sockaddr_in));
64         if (out) {
65             memset(out, 0, sizeof(struct sockaddr_in));
66             ((struct sockaddr_in *)out)->sin_addr = ((struct sockaddr_in *)in)->sin_addr;
67         }
68         break;
69     case AF_INET6:
70         out = malloc(sizeof(struct sockaddr_in6));
71         if (out) {
72             memset(out, 0, sizeof(struct sockaddr_in6));
73             ((struct sockaddr_in6 *)out)->sin6_addr = ((struct sockaddr_in6 *)in)->sin6_addr;
74         }
75         break;
76     }
77     out->sa_family = in->sa_family;
78 #ifdef SIN6_LEN
79     out->sa_len = in->sa_len;
80 #endif
81     return out;
82 }
83
84 char *addr2string(struct sockaddr *addr) {
85     struct sockaddr_in6 *sa6;
86     struct sockaddr_in sa4;
87     static char addr_buf[2][INET6_ADDRSTRLEN];
88     static int i = 0;
89     i = !i;
90     if (addr->sa_family == AF_INET6) {
91         sa6 = (struct sockaddr_in6 *)addr;
92         if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
93             memset(&sa4, 0, sizeof(sa4));
94             sa4.sin_family = AF_INET;
95             sa4.sin_port = sa6->sin6_port;
96             memcpy(&sa4.sin_addr, &sa6->sin6_addr.s6_addr[12], 4);
97             addr = (struct sockaddr *)&sa4;
98         }
99     }
100     if (getnameinfo(addr, SOCKADDRP_SIZE(addr), addr_buf[i], sizeof(addr_buf[i]),
101                     NULL, 0, NI_NUMERICHOST)) {
102         debug(DBG_WARN, "getnameinfo failed");
103         return "getnameinfo_failed";
104     }
105     return addr_buf[i];
106 }
107
108 #if 0
109 /* not in use */
110 int connectport(int type, char *host, char *port) {
111     struct addrinfo hints, *res0, *res;
112     int s = -1;
113     
114     memset(&hints, 0, sizeof(hints));
115     hints.ai_socktype = type;
116     hints.ai_family = AF_UNSPEC;
117
118     if (getaddrinfo(host, port, &hints, &res0) != 0) {
119         debug(DBG_ERR, "connectport: can't resolve host %s port %s", host, port);
120         return -1;
121     }
122
123     for (res = res0; res; res = res->ai_next) {
124         s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
125         if (s < 0) {
126             debug(DBG_WARN, "connectport: socket failed");
127             continue;
128         }
129         if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
130             break;
131         debug(DBG_WARN, "connectport: connect failed");
132         close(s);
133         s = -1;
134     }
135     freeaddrinfo(res0);
136     return s;
137 }
138 #endif
139
140 int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) {
141     int s, on = 1;
142     struct addrinfo *res;
143
144     for (res = addrinfo; res; res = res->ai_next) {
145         if (family != AF_UNSPEC && family != res->ai_family)
146             continue;
147         s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
148         if (s < 0) {
149             debug(DBG_WARN, "bindtoaddr: socket failed");
150             continue;
151         }
152         if (reuse)
153             setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
154         #ifdef IPV6_V6ONLY
155         if (v6only)
156             setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
157         #endif
158         if (!bind(s, res->ai_addr, res->ai_addrlen))
159             return s;
160         debug(DBG_WARN, "bindtoaddr: bind failed");
161         close(s);
162     }
163     return -1;
164 }
165
166 int connectnonblocking(int s, const struct sockaddr *addr, socklen_t addrlen, struct timeval *timeout) {
167     int origflags, error = 0, r = -1;
168     fd_set writefds;
169     socklen_t len;
170     
171     origflags = fcntl(s, F_GETFL, 0);
172     fcntl(s, F_SETFL, origflags | O_NONBLOCK);
173     if (!connect(s, addr, addrlen)) {
174         r = 0;
175         goto exit;
176     }
177     if (errno != EINPROGRESS)
178         goto exit;
179
180     FD_ZERO(&writefds);
181     FD_SET(s, &writefds);
182     if (select(s + 1, NULL, &writefds, NULL, timeout) < 1)
183         goto exit;
184
185     len = sizeof(error);
186     if (!getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&error, &len) && !error)
187         r = 0;
188
189  exit:    
190     fcntl(s, F_SETFL, origflags);
191     return r;
192 }
193
194 int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src, uint16_t timeout) {
195     int s;
196     struct addrinfo *res;
197     struct timeval to;
198
199     s = -1;
200     if (timeout) {
201         if (addrinfo && addrinfo->ai_next && timeout > 5)
202             timeout = 5;
203         to.tv_sec = timeout;
204         to.tv_usec = 0;
205     }
206     
207     for (res = addrinfo; res; res = res->ai_next) {
208         s = bindtoaddr(src, res->ai_family, 1, 1);
209         if (s < 0) {
210             debug(DBG_WARN, "connecttoserver: socket failed");
211             continue;
212         }
213         if ((timeout
214              ? connectnonblocking(s, res->ai_addr, res->ai_addrlen, &to)
215              : connect(s, res->ai_addr, res->ai_addrlen)) == 0)
216             break;
217         debug(DBG_WARN, "connecttoserver: connect failed");
218         close(s);
219         s = -1;
220     }
221     return s;
222 }