1/* @(#) interface/address conversion
2 *
3 * Copyright 2008-2011 Pavel V. Cherenkov (pcherenkov@gmail.com)
4 *
5 *  This file is part of udpxy.
6 *
7 *  udpxy is free software: you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License as published by
9 *  the Free Software Foundation, either version 3 of the License, or
10 *  (at your option) any later version.
11 *
12 *  udpxy is distributed in the hope that it will be useful,
13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *  GNU General Public License for more details.
16 *
17 *  You should have received a copy of the GNU General Public License
18 *  along with udpxy.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22#include <sys/socket.h>
23#include <sys/ioctl.h>
24#include <sys/types.h>
25#include <string.h>
26#include <unistd.h>
27#include <errno.h>
28#include <net/if.h>
29#include <stdlib.h>
30#include <netinet/in.h>
31#include <arpa/inet.h>
32
33#include <assert.h>
34#include <limits.h>
35
36#include "osdef.h"
37#include "ifaddr.h"
38
39
40/* check if ifr contains info on the desired ifname
41 */
42static int
43chkifr( const struct ifreq* ifr, const char* ifname,
44        const size_t addrlen, size_t* offset )
45{
46    size_t sa_len = 0;
47
48    assert(ifr && ifname && offset);
49
50#ifdef NO_SOCKADDR_SA_LEN
51    switch( ifr->ifr_addr.sa_family )
52    {
53    #ifndef NO_INET6_SUPPORT
54        case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break;
55   #endif
56        case AF_INET: sa_len = sizeof(struct sockaddr); break;
57        default: sa_len = 0; break;
58    }
59#else
60    sa_len = ifr->ifr_addr.sa_len;
61#endif
62
63    if( sa_len > 0 ) {
64        if ( (ifr->ifr_addr.sa_family == AF_INET) &&
65            (0 == strncmp(ifname, ifr->ifr_name, sizeof(struct ifreq))) &&
66            (addrlen >= sa_len) ) {
67            *offset = sa_len;
68            return 0;
69        }
70    }
71
72#if defined(__linux)
73    *offset = sizeof(*ifr);
74#else
75    *offset = (sa_len + sizeof( ifr->ifr_name ));
76    /* the above is per R. Stevens' book and not working on 64-bit Linux */
77#endif
78
79    return -1;
80}
81
82
83
84/* retrieve IPv4 address of the given network interface
85 */
86int
87if2addr( const char* ifname,
88             struct sockaddr *addr, size_t addrlen )
89{
90    int rc, sockfd;
91    char *buf, *rec;
92    size_t buflen, offset;
93    int last_len;
94    struct ifconf  ifc;
95    struct ifreq   ifr;
96
97    static size_t IFC_TABLE_SIZE;
98
99    static const size_t IFC_ENTRIES = 32;
100    static const size_t MAX_IFCBUF_SIZE = (1024 * 256);
101
102    IFC_TABLE_SIZE = sizeof(struct ifreq) * IFC_ENTRIES;
103
104    assert( ifname && addr && addrlen );
105    rc = 0;
106
107    /* acquire the list of network interfaces */
108
109    sockfd = socket( AF_INET, SOCK_DGRAM, 0 );
110    if( -1 == sockfd ) return -1;
111
112    buf = NULL; buflen = IFC_TABLE_SIZE; last_len = 0;
113    for( ; buflen < MAX_IFCBUF_SIZE; buflen += IFC_TABLE_SIZE ) {
114        if( NULL == (buf = malloc( buflen )) ) {
115            rc = -1;
116            break;
117        }
118
119        ifc.ifc_len = buflen;
120        ifc.ifc_buf = buf;
121        if( ioctl( sockfd, SIOCGIFCONF, &ifc ) < 0 ) {
122            if( (EINVAL != errno) || (last_len != 0) ) {
123                rc = errno;
124                break;
125            }
126        }
127        else {
128            if( ifc.ifc_len == last_len )
129                break;
130            else
131                last_len = ifc.ifc_len;
132        }
133
134        free( buf );
135        buf = NULL;
136    } /* for */
137
138    (void) close( sockfd );
139    if( buflen > MAX_IFCBUF_SIZE ) rc = -1;
140
141    if( 0 != rc ) {
142        if( NULL != buf ) free( buf );
143        return rc;
144    }
145
146    assert( ifc.ifc_buf );
147
148    /* look for ifname in the list */
149
150    for( rec = ifc.ifc_buf; rec < (ifc.ifc_buf + ifc.ifc_len); ) {
151        (void) memcpy( &ifr, rec, sizeof(struct ifreq) );
152
153        offset = 0;
154        rc = chkifr( &ifr, ifname, addrlen, &offset );
155        if ( 0 == rc ) {
156            (void) memcpy( addr, &(ifr.ifr_addr), offset );
157            break;
158        }
159
160        if( 0 == offset ) break;
161        rec += offset;
162    } /* for */
163
164    if( rec >= (buf + ifc.ifc_len) ) {
165        rc = -1;
166    }
167
168    free( buf );
169    return rc;
170}
171
172
173/* convert input parameter into an IPv4-address string
174 */
175int
176get_ipv4_address( const char* s, char* buf, size_t len )
177{
178    struct sockaddr_in saddr;
179    int rc = 0;
180
181    assert( s && buf && len );
182
183    if( 1 == inet_aton(s, &(saddr.sin_addr)) ) {
184        (void) strncpy( buf, s, len );
185    }
186    else {
187        rc = if2addr( s, (struct sockaddr*)&saddr, sizeof(saddr) );
188        if( 0 != rc ) return rc;
189
190        (void) strncpy( buf, inet_ntoa(saddr.sin_addr), len );
191    }
192
193    buf[ len - 1 ] = 0;
194    return rc;
195}
196
197
198/* split input string into IP address and port
199 */
200int
201get_addrport( const char* s, char* addr, size_t len, int* port )
202{
203    struct sockaddr_in saddr;
204    size_t i = 0;
205    int iport = 0;
206
207    static const int ERR_NOPORT     = -1;
208    static const int ERR_BADADDR    = -2;
209    static const int ERR_BADPORT    = -3;
210    static const int ERR_OVERFLOW   = -4;
211
212    assert( s && addr && len && port );
213
214    for( i = 0; (i < len) && s[i] && (':' != s[i]); ++i )
215        addr[ i ] = s[ i ];
216    if( i >= len )
217        return ERR_OVERFLOW;
218    else
219        addr[i] = '\0';
220
221    /* IP address is not followed by port */
222    if( ':' != s[ i ] ) return ERR_NOPORT;
223
224    if( 1 != inet_aton( addr, &(saddr.sin_addr)) )
225        return ERR_BADADDR;
226
227    ++i;
228    if( i >= len || !s[i] ) return ERR_NOPORT;
229
230    errno = 0;
231    iport = atoi( s + i );
232    if( errno || (iport <= 0) || (iport > (int)USHRT_MAX) )
233        return ERR_BADPORT;
234
235    *port = iport;
236    return 0;
237}
238
239
240/* __EOF__ */
241
242